ithans

Float on the sea

Study React

React 学习笔记

A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES

引言是 React 官网 上是的自述。React 主要是用于构建用户界面。

为什么使用 React?

React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库。主要采用以下两个思想:

  • 简单

    仅仅只要表达出你的应用程序在任一个时间点应该长的样子,然后当底层的数据变了,React 会自动处理所有用户界面的更新。

  • 声明式

    数据变化后,React 概念上与点击“刷新”按钮类似,但仅会更新变化的部分。

几个特点

  • 仅仅是 UI

    许多人使用 React 作为 MVC 架构的 V 层。尽管 React 并没有假设过你的其他技术栈,但你仍可以作为一个小特征轻易地在已有项目中使用。

  • 虚拟 DOM

    React为了更高超的性能而使用虚拟DOM作为其不同的实现。 它同时也可以由服务端Node.js渲染 - 而不需要过重的浏览器DOM支持。

  • 数据流

    React实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

几个概念

  • React elements

    React 元素(React elements) 是一些用于表示 HTML 元素的 JavaScript 对象,他们并不存在于浏览器中,浏览器原生对象在 React 中,也有对应的 React 元素对应,比如 h1divsection

  • Components

    组件(Components)也就是我们常说的组件,他们一般是构成我们页面的最大的一部分,主要包括「结构」和「功能」。举个例子,比如 NavBarLikeButtonImageUploader 都是一个个带有结构和功能的组件。

    组件就像是函数。接受propsstate作为参数,渲染出HTML

  • JSX

    JSX 是一个看起来想 XML 的 JavaScript 语法扩展。举个例子:<h1>Hello</h1>React Element 使用 JSX 的语法的写法。同样的,如果你不用 JSX ,使用这样的写法:React.DOM.h1(null, 'Hello') 也可以达到相同的效果。相比之下 JSX 的易读性更好。 JSX 需要编译之后才能在浏览器中运行。JSX并不是强制使用的,这个语法只是让搭建 React 应用变得更加地简单。

  • The Virtual DOM

    虚拟 DOM(The Virtual DOM) 是存在于内存之中的一种数据结构,并不是真正的 DOM 节点。只有当他插入到文档中以后,才会变成真正的 DOM 。React 会监听虚拟 DOM 的变化,会自动的计算 DOM 和 虚拟 DOM 的区别。所有 DOM 的变动,都会先在 虚拟 DOM 上发生,然后再将实际变动的部分,反应在真实的 DOM 上,这种算法叫 DOM diff ,它极大的提高了网页性能表现。

需要先稍微理解下以上的几个概念,方便

渲染

我们首先来看下怎么把 虚拟 DOM (React 元素和组件都是虚拟 DOM)渲染到页面上。我们知道,虚拟 DOM 只存在于内存中,我们需要告诉 React ,以让它执行虚拟 DOM 的渲染。

1
React.render(<h1>Hello World</h1>, document.getElementById('app'));

上面的代码会将一个 H1 ,插入到 body 上。

render 函数接收两个参数,第一个参数为 React element,第二个参数为 DOM element

组件

组件是 React 的重要组成部分,是自定义的 React 元素。一个组件就是一个功能。

1
2
3
4
5
6
7
var HelloWorld = React.createClass({
  render: fucntion(){
      return (<h1>Hello World!</h1>)
  }
})

React.render(<HelloWorld />, document.getElementById('app'));

createClass 方法的参数是个对象,这个对象需要包含 render函数。

在上段代码中,通过 createClass 我们创建了 HelloWorld 元素,然后我们通过 React.render 方法将 HelloWorld 元素渲染到了 #app 里面。

这个例子和上一个差不多,区别是你可以为 HelloWorld 元素添加更多的自定义方法和逻辑了。

注意 自定义的 React 元素,第一个字母需要大写。

Props

Props 我们可以理解为组件的参数。通过 HTML 的属性传入。

1
2
3
4
5
6
7
var HelloWorld = React.createClass({
  render: fucntion(){
      return (<h1>Hello World! {this.props.author}</h1>)
  }
})

React.render(<HelloWorld author='somebody'/>, document.getElementById('app'));

React.render 方法中,我们传入了 author 这个参数。这个参数在 HelloWorld 组件的 render 通过 this.props.author 取得。

所有通过 ReactElement 传入的参数,都可以在组件的 render 方法中获取到。值得注意的是,ReactElement 是通过 JavaScript 创建的,class for 等均为 JavaScript 的保留字,在 JSX 中不能直接写 classfor,而需要写成 classNamehtmlFor

1
React.render(<label htmlFor="inputEl"><input type="text" className="input-el" id="inputEl"/></label>)

this.props.children

this.props 对象的属性和组件的属性一一对应,但是有一个是例外的, 就是 this.props.children 。它表示的是组件的所有子节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var List = React.createClass({
  render: function() {
    return (
      <ul>
      {
        this.props.children.map(function (child) {
          return <li>{child}</li>
        })
      }
      </ol>
    );
  }
});

React.render(<list><span>Hello</span><span>World</span></list>)

注意:你没有办法通过 this.props.children 取得当前组件的子元素,因为 this.props.children 返回的是组件传递给你的 Passed onto you 子节点。

iframe-size-not-work-in-IOS

最近遇到一个问题,就是iFrame在iPad上展示的时候,宽度并没有固定,会直接超出,通过嵌套div之后,无法滚动。

HTML

1
2
3
4
5
<section class="frame_holder">
  <iframe class="my_frame">
    // The content
  </iframe>
</section>

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
body {
  position: relative;
  background: #f0f0f0;
}

.frame_holder {
  position: absolute;
  top: 50px;
  bottom: 50px;
  left: 50px;
  right: 50px;
  background: #ffffff;
}

.my_frame {
  width: 100%;
  height: 100%;
  border: 1px solid #e0e0e0;
}

例子:

http://jsfiddle.net/R3PKB/2/

可以在你的iPad上去看看这个页面

http://jsfiddle.net/R3PKB/2/embedded/result

解决方案是:

在外层的div上添加overflow:auto-webkit-overflow-scrolling:touch就可以了。

http://jsfiddle.net/R3PKB/7/

参考链接:

周末有感

今天晚上跟女朋友去UME看了《速度与激情6》,很精彩。(那边的饮料真是坑,12块钱一瓶)

回来的路上,两人谈论起我喜欢的美国大片和她喜欢的感情戏。结果是,我觉得她说的话还是比较有道理的,她是这么说的:

我看的电视剧改变了我很多,一些人生观价值观的东西,人生并不是为了一个目标而或者,并不是说,你赚到一个亿的时候才是你开心的时候,其他的时间都不开心,就算你真的赚到你一个亿了,你还是失去了之前的快乐。无论我们工作、学习,都是为了更加快乐的生活。

“人生得意须尽欢,莫使金樽空对月”,再回想之前外包转正面试失败这件事情,其实未必也不是件好事,这次面试,认识了很多新的东西,知道自己有很多不足的地方,得到了一些前辈的指点,收获很大。结果,有时候往往是顺路而来的。

今天凌晨看了《精忠岳飞》的63,64集,岳飞征战一身,最后还是因为皇帝的疑心和嫉妒以及秦桧的谗言,白白葬送了十余年的胜利。《满江红·怒发冲冠》中有一句“莫等闲,白了少年头,空悲切”,很喜欢,也很心痛。


伊泽
2013-07-28于杭州

Javascript 折半搜索法(二分法)

周末,写个简单的算法。

JS二分法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Array.prototype.binarySearch = function (v) {

  var l = 0;
  var h = this.length - 1;

  while (l<= h) {
      var m = l + ((h - l) >> 1);
      console.log('l@%s,h@%s,m@%s', l, h, m);
      if (this[m] === v) {
          return m;
      }
      if (this[m] > v) {
          h = m - 1;
      } else {
          l = m + 1;
      }
  }

  return -(l + 1);
}

使用:

1
2
3
4
5
6
7
var arr = [1, 2, 3, 5, 78, 2312, 424];

arr.sort(function (x, y) {
  return x - y;
})
console.log(arr);
console.log(arr.binarySearch(424));

说明:

var m = l + ((h - l) >> 1);,为什么要用位运算?

其实用var m = Math.floor((h+l)/2);也是可以的,但是位运算的速度优于除法,并且还要再进行一次Math.floor。用位运算主要是出于性能考虑。h+l还存在溢出的问题,不过这个在web端应该不会遇到。。。Node.js就有可能了。

还有个复杂度的问题,下回再分析。


伊泽
2013-07-29于杭州

node.js Connection Lost the Server Closed the Connection.

mysql模块

今天访问了下内网的一台服务器,发现挂掉了,SSH上去看了下nohup.out,发现报错了

1
2
3
4
5
6
7
8
events.js:71
        throw arguments[1]; // Unhandled 'error' event
                       ^
Error: Connection lost: The server closed the connection.
    at Protocol.end (/var/www/html/utci/node_modules/mysql/lib/protocol/Protocol.js:73:13)
    at Socket.onend (stream.js:66:10)
    at Socket.EventEmitter.emit (events.js:126:20)
    at TCP.onread (net.js:417:51)

这个错误的大概意思就是,未处理的error事件

原因是:mysql的连接久了以后,超时了。

解决方法是:

增加error事件的监听

database.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var mysql = require('mysql'),
    settings = require('../settings');

module.exports.getConnection = function () {
    if ((module.exports.connection) && (module.exports.connection._socket)
        && (module.exports.connection._socket.readable)
        && (module.exports.connection._socket.writable)) {
        return module.exports.connection;
    }
    console.log(((module.exports.connection) ?
        "UNHEALTHY SQL CONNECTION; RE" : "") + "CONNECTING TO SQL.");
    var connection = mysql.createConnection({
        host: settings.host,
        port: settings.port,
        database: settings.db_name,
        user: settings.username,
        password: settings.password,
        //中文读取
        charset: "utf8"
    });
    connection.connect(function (err) {
        if (err) {
            console.log("SQL CONNECT ERROR: " + err);
        } else {
            console.log("SQL CONNECT SUCCESSFUL.");
        }
    });
    connection.on("close", function (err) {
        console.log("SQL CONNECTION CLOSED.");
    });
    connection.on("error", function (err) {
        console.log("SQL CONNECTION ERROR: " + err);
    });
    module.exports.connection = connection;
    return module.exports.connection;
};

// Open a connection automatically at app startup.
module.exports.getConnection();
// If you've saved this file as database.js, then get and use the
// connection as in the following example:
// var database = require(__dirname + "/database");
// var connection = database.getConnection();
// connection.query(query, function(err, results) { ....

伊泽
2013-07-07于杭州