React,当下最炙手可热的前端框架,我们描述得最多的就是它拥有优越的渲染性能和高质量的模块化。那它长什么样子,如何来使用呢?以下是我从以前的自己的文章汇总起来的,跨越了几个版本,因此,很有必要说明当前版本号,也顺便追踪一下其变化过程。我先以最早之前看的版本v0.13.0来开始吧。

快速开始

先看一个最简单的 react 项目目录

1
2
3
4
5
react-study
├── build
| ├── JSXTransformer.js
| └── react.min.js
└── hellowworld.html

以打印经典的 hello world 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- helloworld.html -->
<!DOCTYPE html>
<html>
<head>
<script src="build/react.js"></script>
<script src="build/JSXTransformer.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/jsx">
React.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>

</body>
</html>

上面的 text/jsx 中的代码是JSX语法的代码,跟浏览器并不兼容,可以通过 JSXTransformer.js 将JSX代码转化为标准的JS代码。但并不建议我们这么做,这样很耗性能,我们可以将这个工作交给幕后,而不是加载页面的时候进行。

创建 src/helloworld.js,html中的把jsx转移到里面

1
2
3
4
5
6
7
react-study
├── build
| ├── JSXTransformer.js
| └── react.min.js
├── src
| ├── helloworld.js
└── hellowworld.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<script src="build/react.js"></script>
<script src="build/JSXTransformer.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/jsx" src="src/helloworld.js"></script>
</body>
</html>
1
2
3
4
5
// src/helloworld.js
React.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

但某些浏览器(如,Chrome浏览器)将无法加载该文件,除非它使用HTTP服务。

安装命令行工具(依赖 npm)

1
sudo npm install -g react-tools

将 src/helloworld.js 文件转成标准的 javascript,并检测文件目录里的修改变动,自动更新

1
jsx --watch src/ build/
1
2
3
4
5
6
7
8
react-study
├── build
| ├── helloworld.js
| ├── JSXTransformer.js
| └── react.min.js
├── src
| ├── helloworld.js
└── hellowworld.html
1
2
3
4
5
// build/helloworld.js
React.render(
React.createElement('h1', null, 'Hello, world!'),
document.getElementById('example')
);

更正 html,完成

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<title>Hello React!</title>
<script src="build/react.js"></script>
</head>
<body>
<div id="example"></div>
<script src="build/helloworld.js"></script>
</body>
</html>

最简单的 React 栗子描述完了,给我的第一印象是和less/sass的引进使用方式有点相近。

核心基本函数方法之一,React.render(),结构渲染

React.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。

1
2
3
4
React.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

该代码实现 h1 标签插入到 example 标签中

1
2
3
4
5
6
7
8
9
10
11
var names = ['Alice', 'Emily', 'Kate'];
ReactDOM.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,

document.getElementById('example')
);

上面代码体现了 JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析

1
2
3
4
5
6
7
8
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);

JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员

核心基本函数方法之二,React.createClass(),创建组件

1
2
3
4
5
6
7
8
9
10
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});

React.render(
<HelloMessage name="John" />,
document.getElementById('example')
);

1)上面代码中,变量 HelloMessage 就是一个组件类。模板插入 时,会自动生成 HelloMessage 的一个实例。所有组件类都必须有自己的 render 方法,用于输出组件。
2)组件类的第一个字母必须大写,否则会报错,比如HelloMessage不能写成helloMessage。
3)另外,组件类只能包含一个顶层标签,否则也会报错。

1
2
3
4
5
6
7
8
9
10
var HelloMessage = React.createClass({
render: function() {
return <h1>
Hello {this.props.name}
</h1>
<p>
some text
</p>;
}
});

上面代码会报错,因为HelloMessage组件包含了两个顶层标签:h1和p。

然后到了React v0.14.7,相比Reat v0.13.0有一些明显的变动。如原本的 react package 被拆分为react及react-dom两个package。其中react package 中包含React.createElement、React.createClass、React.Component,React.PropTypes,React.Children这些API,而react-dom package中包含ReactDOM.render、React.unmountComponentAtNode、React.findDOMNode。React的html结构大致如此:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel" src="../src/helloworld.js"></script>
</body>
</html>

引入库的变化

其中,react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能,Browser.js 跟JSXTransformer.js的作用类似,是将 JSX 语法转为 JavaScript 语法,同样,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成。

离线转换工具的变化

Babel 代替JSX,首先你得确保安装 Babel命令行工具,注意Babel和Babel命令行工具远不是一回事哦:

安装babel命令行工具

1
npm install -g babel-cli

编译脚本

1
babel src --out-dir build

这样一来,html变成:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
</head>
<body>
<div id="example"></div>
<script src="../build/helloworld.js"></script>
</body>
</html>

ReactDOM.render 代替 React.render

1
2
3
4
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

之前,我们只认识了 React.createClass() 、和 ReactDOM.render() 这两个基本函数,这次,我们再来认识一下几个常用概念,版本v0.14.7。

this.props

React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。

1
2
3
4
5
6
7
8
9
10
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});

ReactDOM.render(
<HelloMessage name="John" />,
document.getElementById('example')
);

上面代码中,变量 HelloMessage 就是一个组件类。模板插入 时,会自动生成 HelloMessage 的一个实例。所有组件类都必须有自己的 render 方法,用于输出组件。
组件的用法与原生的 HTML 标签完全一致,可以任意加入属性,比如 ,就是 HelloMessage 组件加入一个 name 属性,值为 John。组件的属性可以在组件类的 this.props 对象上获取,比如 name 属性就可以通过 this.props.name 读取。
添加组件属性,有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

this.props.children

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>

);
}
});

ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,

document.body
);

上面代码的 NoteList 组件有两个 span 子节点,它们都可以通过 this.props.children 读取。
这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。
React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。

React.Children

React.Children 为处理 this.props.children 这个封闭的数据结构提供了有用的工具。

React.Children.map

1
object React.Children.map(object children, function fn [, object context])

在每一个直接子级(包含在 children 参数中的)上调用 fn 函数,此函数中的 this 指向 上下文。如果 children 是一个内嵌的对象或者数组,它将被遍历:不会传入容器对象到 fn 中。如果 children 参数是 null 或者 undefined,那么返回 null 或者 undefined 而不是一个空对象。

React.Children.forEach

1
React.Children.forEach(object children, function fn [, object context])

类似于 React.Children.map(),但是不返回对象。

React.PropTypes

组件类的ProTypes属性,用来验证使用组件时提供的属性值是否符合要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},

render: function() {
return <h1> {this.props.title} </h1>;
}
});

ReactDOM.render(
<MyTitle title="123" />,
document.body
);

上面的Mytitle组件有一个title属性。PropTypes 告诉 React,这个 title 属性是必须的,而且它的值必须是字符串。像上面这样输入数字,控制台就会报错。

此外,getDefaultProps 方法可以用来设置组件属性的默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},

render: function() {
return <h1> {this.props.title} </h1>;
}
});

ReactDOM.render(
<MyTitle />,
document.body
);

上面代码会输出”Hello World”。

目前React的版本到了15.1.0,这个其实是官方宣布将版本的小数点向左移了一位,此前0.14.x称为React 14,说是为避免开头的0的误解以及提升这个迅猛发展的项目的认可度。

React的基础核心内容其实就是上面这些,更多内容请查看官方网站