性能分析工具

最后更新于:2022-04-01 20:48:18

通常情况下,React在沙箱中是非常快的。但是,在你应用的一些情景中,你需要仔细推敲每一个性能点。React提供了一个函数[shouldComponentUpdate](http://reactjs.cn/react/docs/component-specs.html#updating-shouldcomponentupdate),通过这个函数,你能够给React的差异检查添加优化代码。 为了给你一个你的应用总体的性能概览,ReactPerf是一个分析工具,告诉你需要把这些钩子函数放在哪里。 > 注意: > > 开发的构建过程比生产的构建过程要慢,是因为所有额外逻辑的提供,例如,友好的控制台警告(生产构建时会去掉)。因此,分析工具仅用于指出你应用中相对影响性能的部分。 ## 通用API 当使用`react-with-addons.js`在开发模式下构建的时候,这里描述的`Perf`对象是以`React.addons.Perf`的形式暴露出来的。 ### `Perf.start()`和`Perf.stop()` 开始/停止检测。React的中间操作被记录下来,用于下面的分析。花费一段微不足道的时间的操作会被忽略。 ### `Perf.printInclusive(measurements)` 打印花费的总时间。如果不传递任何参数,默认打印最后的所有检测记录。它会在控制台中打印一个漂亮的格式化的表格,像这样: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-20_55acbeeb2d924.png) ### `Perf.printExclusive(measurements)` “独占(Exclusive)”时间不包含挂载组件的时间:处理props,`getInitialState`,调用`componentWillMount`和`componentDidMount`,等等。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-20_55acbeeb4bd59.png) ### `Perf.printWasted(measurements)` **分析器最有用的部分**。 “浪费”的时间被花在根本没有渲染任何东西的组件上,例如界面渲染后保持不变,没有操作DOM元素。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-20_55acbeeb7a81b.png) ### `Perf.printDOM(measurements)` 打印底层DOM操作,例如,“设置innerHTML”和“移除节点”。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-20_55acbeebaf479.png) ## 高级API 上面的打印方法使用`Perf.getLastMeasurements()`打印好看的结果。 ### `Perf.getLastMeasurements()` 从最后的开始-停止会话中得到检测结果数组。该数组包含对象,每个对象看起来像这样: ~~~ { // The term "inclusive" and "exclusive" are explained below "exclusive": {}, // '.0.0' is the React ID of the node "inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346}, "render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559}, // Number of instances "counts": {".0": 1, ".0.0": 1}, // DOM touches "writes": {}, // Extra debugging info "displayNames": { ".0": {"current": "App", "owner": ""}, ".0.0": {"current": "Box", "owner": "App"} }, "totalTime": 0.48499999684281647 } ~~~
';

PureRenderMixin

最后更新于:2022-04-01 20:48:16

如果你的React组件的渲染函数是“纯粹的”(换句话说,当传给它同样的props和state,它渲染出同样的效果),在某些场景下,你可以利用这个插件来极大地提升性能。 例如: ~~~ var PureRenderMixin = require('react').addons.PureRenderMixin; React.createClass({ mixins: [PureRenderMixin], render: function() { return
foo
; } }); ~~~ 在底层,该插件实现了[shouldComponentUpdate](http://reactjs.cn/react/docs/component-specs.html#updating-shouldcomponentupdate),在这里面,它比较当前的props、state和接下来的props、state,当两者相等的时候返回`false`。 > 注意: > > 仅仅是浅比较对象。如果对象包含了复杂的数据结构,深层次的差异可能会产生误判。仅用于拥有简单props和state的组件,或者当你知道很深的数据结构已经变化了的时候使用`forceUpdate()`。或者,考虑使用[immutable objects](http://facebook.github.io/immutable-js/)来帮助嵌套数据快速比较。 > > 此外,`shouldComponentUpdate`会跳过更新整个组件子树。确保所有的子组件也是“纯粹的”。
';

不可变数据的辅助工具(Immutability Helpers)

最后更新于:2022-04-01 20:48:13

React让你可以使用任何你想要的数据管理风格,包括数据可变风格。然而,如果你能够在你应用中讲究性能的部分使用不可变数据,就可以很方便地实现一个快速的`shouldComponentUpdate()`方法来显著提升你应用的速度。 在JavaScript中处理不可变数据比在语言层面上就设计好要难,像[Clojure](http://clojure.org/)。但是,我们提供了一个简单的不可变辅助工具,`update()`,这就让处理这种类型的数据更加简单了,根本_不会_改变你数据的表示的形式。(Dealing with immutable data in JavaScript is more difficult than in languages designed for it, like [Clojure](http://clojure.org/). However, we've provided a simple immutability helper, `update()`, that makes dealing with this type of data much easier, _without_fundamentally changing how your data is represented.) ## 主要思想(The main idea) 如果你像这样改变数据: ~~~ myData.x.y.z = 7; // or... myData.a.b.push(9); ~~~ 你无法确定哪个数据改变了,因为之前的副本被覆盖了。相反,你需要创建一个新的`myDate`副本,仅仅改变需要改变的部分。然后你就能够在`shouldComponentUpdate()`中使用第三方的相等判断来比较`myData`的旧副本和新对象: ~~~ var newData = deepCopy(myData); newData.x.y.z = 7; newData.a.b.push(9); ~~~ 不幸的是,深拷贝是很昂贵的,而且某些时候还不可能完成。你可以通过仅拷贝需要改变的对象,重用未改变的对象来缓解这个问题。不幸的是,在当今的JavaScript里面,这会变得很笨拙: ~~~ var newData = extend(myData, { x: extend(myData.x, { y: extend(myData.x.y, {z: 7}), }), a: extend(myData.a, {b: myData.a.b.concat(9)}) }); ~~~ 虽然这能够非常好地提升性能(因为仅仅浅复制`log n`个对象,重用余下的),但是写起来很痛苦。看看所有的重复书写!这不仅仅是恼人,也提供了一个巨大的出bug的区域。 `update()`在这种情形下提供了简单的语法糖,使得写这种代码变得更加简单。代码变为: ~~~ var newData = React.addons.update(myData, { x: {y: {z: {$set: 7}}}, a: {b: {$push: [9]}} }); ~~~ 虽然这种语法糖需要花点精力适应(尽管这是受[MongoDB's query language](http://docs.mongodb.org/manual/core/crud-introduction/#query)的启发),但是它没有冗余,是静态可分析的,并且比可变的版本少打了很多字。(While the syntax takes a little getting used to (though it's inspired by [MongoDB's query language](http://docs.mongodb.org/manual/core/crud-introduction/#query)) there's no redundancy, it's statically analyzable and it's not much more typing than the mutative version.) 以`$`为前缀的键被称作_命令_。他们“改变”的数据结构被称为_目标_。( The `$`-prefixed keys are called _commands_. The data structure they are "mutating" is called the _target_.) ## 可用的命令(Available commands) * `{$push: array}` 利用`push()`把目标上所有的元素放进`数组`(`push()` all the items in `array` on the target.)。 * `{$unshift: array}` 利用`unshift()`把目标上所有的元素放进`数组`(`unshift()` all the items in `array` on the target.)。 * `{$splice: array of arrays}` 对于`array`中的每一个元素,用元素提供的参数在目标上调用`splice()`(for each item in `arrays` call `splice()` on the target with the parameters provided by the item.)。 * `{$set: any}` 整体替换目标(replace the target entirely.)。 * `{$merge: object}` 合并目标和`object`的键。 * `{$apply: function}` 传入当前的值到函数,然后用新返回的值更新它(passes in the current value to the function and updates it with the new returned value.)。 ## 示例 ### 简单的入栈 ~~~ var initialArray = [1, 2, 3]; var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4] ~~~ `initialArray`仍然是`[1, 2, 3]`。 ### 嵌入的集合 ~~~ var collection = [1, 2, {a: [12, 17, 15]}]; var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}}); // => [1, 2, {a: [12, 13, 14, 15]}] ~~~ 获取`collection`中索引是`2`的对象,然后取得该对象键为`a`的值,删掉索引从`1`开始的一个元素(即移除`17`),插入`13`和`14`。(This accesses `collection`'s index `2`, key `a`, and does a splice of one item starting from index `1` (to remove `17`) while inserting `13` and`14`.) ### 根据现有的值更新 ~~~ var obj = {a: 5, b: 3}; var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}}); // => {a: 5, b: 6} // This is equivalent, but gets verbose for deeply nested collections: var newObj2 = update(obj, {b: {$set: obj.b * 2}}); ~~~ ### (浅)合并 ~~~ var obj = {a: 5, b: 3}; var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7} ~~~
';

克隆组件

最后更新于:2022-04-01 20:48:11

在极少数应用场景中,一个组件可能想改变另一个它不拥有的组件的props(就像改变一个组件的`className`,这个组件又作为`this.props.children`传入)。其它的时候,可能想生成传进来的一个组件的多个拷贝。`cloneWithProps()`使其成为可能。 ### ReactComponent React.addons.cloneWithProps(ReactComponent component, object? extraProps) 做一个`component`的浅复制,合并`extraProps`提供的每一个props。`className`和`style`props将会被智能合并。 > 注意: > > `cloneWithProps`并不传递`key`到克隆的组件中。如果你希望保留key,将其添加到`extraProps`对象: `js var clonedComponent = cloneWithProps(originalComponent, { key : originalComponent.key }); ``ref`也一样不会保留。
';

测试工具集

最后更新于:2022-04-01 20:48:09

`React.addons.TestUtils`使得在你选择的测试框架中测试React组件变得简单(我们使用[Jest](http://facebook.github.io/jest/))。 ### 模拟 ~~~ Simulate.{eventName}(DOMElement element, object eventData) ~~~ 模拟事件在DOM节点上派发,附带可选的`eventData`事件数据。**这可能是在`ReactTestUtils`中最有用的工具。** 使用示例: ~~~ var node = this.refs.input.getDOMNode(); React.addons.TestUtils.Simulate.click(node); React.addons.TestUtils.Simulate.change(node, {target: {value: 'Hello, world'}}); React.addons.TestUtils.Simulate.keyDown(node, {key: "Enter"}); ~~~ `Simulate`有一个方法适用于每个事件,这些事件都是React能识别的。 ### renderIntoDocument ~~~ ReactComponent renderIntoDocument(ReactComponent instance) ~~~ 把一个组件渲染成一个在文档中分离的DOM节点。**这个函数需要DOM。** ### mockComponent ~~~ object mockComponent(function componentClass, string? mockTagName) ~~~ 传递一个虚拟的组件模块给这个方法,给这个组件扩充一些有用的方法,让组件能够被当成一个React组件的仿制品来使用。这个组件将会变成一个简单的``(或者是其它标签,如果`mockTagName`提供了的话),包含任何提供的子节点,而不是像往常一样渲染出来。 ### isElementOfType ~~~ boolean isElementOfType(ReactElement element, function componentClass) ~~~ 如果`element`是一个类型为React `componentClass`的React元素,则返回true。 ### isDOMComponent ~~~ boolean isDOMComponent(ReactComponent instance) ~~~ 如果`instance`是一个DOM组件(例如``或者``),则返回true。 ### isCompositeComponent ~~~ boolean isCompositeComponent(ReactComponent instance)` ~~~ 如果`instance`是一个合成的组件(通过`React.createClass()`创建),则返回true。 ### isCompositeComponentWithType ~~~ boolean isCompositeComponentWithType(ReactComponent instance, function componentClass) ~~~ 如果`instance`是一个合成的组件(通过`React.createClass()`创建),此组件的类型是React `componentClass`,则返回true。 ### findAllInRenderedTree ~~~ array findAllInRenderedTree(ReactComponent tree, function test) ~~~ 遍历`tree`中所有组件,搜集`test(component)`返回true的所有组件。就这个本身来说不是很有用,但是它可以为其它测试提供原始数据。 ### scryRenderedDOMComponentsWithClass ~~~ array scryRenderedDOMComponentsWithClass(ReactComponent tree, string className) ~~~ 查找组件的所有实例,这些实例都在渲染后的树中,并且是带有`className`类名的DOM组件。 ### findRenderedDOMComponentWithClass ~~~ ReactComponent findRenderedDOMComponentWithClass(ReactComponent tree, string className) ~~~ 类似于`scryRenderedDOMComponentsWithClass()`,但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。 ### scryRenderedDOMComponentsWithTag ~~~ array scryRenderedDOMComponentsWithTag(ReactComponent tree, string tagName) ~~~ 在渲染后的树中找出所有组件实例,并且是标签名字符合`tagName`的DOM组件。 ### findRenderedDOMComponentWithTag ~~~ ReactComponent findRenderedDOMComponentWithTag(ReactComponent tree, string tagName) ~~~ 类似于`scryRenderedDOMComponentsWithTag()`,但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。 ### scryRenderedComponentsWithType ~~~ array scryRenderedComponentsWithType(ReactComponent tree, function componentClass) ~~~ 找出所有组件实例,这些组件的类型为`componentClass`。 ### findRenderedComponentWithType ~~~ ReactComponent findRenderedComponentWithType(ReactComponent tree, function componentClass) ~~~ 类似于`scryRenderedComponentsWithType()`,但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。
';

类名操作

最后更新于:2022-04-01 20:48:07

`classSet()`是一个简洁的工具,用于简单操作DOM中的`class`字符串。 这里是一个常见的场景,处理方式中没有使用`classSet()`: ~~~ // inside some `` React component render: function() { var classString = 'message'; if (this.props.isImportant) { classString += ' message-important'; } if (this.props.isRead) { classString += ' message-read'; } // 'message message-important message-read' return
Great, I'll be there.
; } ~~~ 这会很快变得单调乏味,因为指定类名的代码很难阅读,并且容易出错。`classSet()`解决了这个问题: ~~~ render: function() { var cx = React.addons.classSet; var classes = cx({ 'message': true, 'message-important': this.props.isImportant, 'message-read': this.props.isRead }); // same final string, but much cleaner return
Great, I'll be there.
; } ~~~ 当使用`classSet()`的时候,传递一个对象,对象上的键是你需要或者不需要的CSS类名。对应真值的键将会成为结果字符串的一部分。 `classSet`也允许传递一些类名作为参数,然后拼接这些类名: ~~~ render: function() { var cx = React.addons.classSet; var importantModifier = 'message-important'; var readModifier = 'message-read'; var classes = cx('message', importantModifier, readModifier); // Final string is 'message message-important message-read' return
Great, I'll be there.
; } ~~~ 没有更多需要钻研的字符串拼接!
';

双向绑定辅助工具

最后更新于:2022-04-01 20:48:04

`ReactLink`是一种简单表达React双向绑定的方式。 > 注意: > > 如果你是这个框架的初学者,记住`ReactLink`对于大多数应用来说都是不需要的,应该谨慎使用。 在React里面,数据流是一个方向的:从拥有者到子节点。这是因为根据[the Von Neumann model of computing](http://en.wikipedia.org/wiki/Von_Neumann_architecture),数据仅向一个方向传递。你可以认为它是`单向数据绑定`。 然而,有很多应用需要你读取一些数据,然后传回给你的程序。例如,在开发表单的时候,当你接收到用户输入时,你将会频繁地想更新某些React `state`。或者你想在JavaScript中演算布局,然后反应到某些DOM元素的尺寸上。 在React中,你可以通过监听一个“change”事件来实现这个功能,从你的数据源(通常是DOM)读取,然后在你某个组件上调用`setState()`。"关闭数据流循环"明显会引导写出更加容易理解的和维护的程序。查看[我们的表单文档](http://reactjs.cn/react/docs/forms.html)来获取更多信息。 双向绑定 -- 隐式地强制在DOM里面的数据总是和某些React `state`保持一致 -- 是简明的,并且支持非常多的应用。我们已经提供了`ReactLink`:如上所述,是一种设置通用数据流循环模型的语法糖,或者说“关联”某些数据到React `state`。 > 注意: > > ReactLink仅仅是一个`onChange`/`setState()`模式的简单包装和约定。它不会从根本上改变数据在你的React应用中如何流动。 ## ReactLink: 前后对比 这是一个简单的表单示例,没有使用`ReactLink`: ~~~ var NoLink = React.createClass({ getInitialState: function() { return {message: 'Hello!'}; }, handleChange: function(event) { this.setState({message: event.target.value}); }, render: function() { var message = this.state.message; return ; } }); ~~~ 这段代码运行地很好,数据如何流动是非常清晰的,但是,如果表单有大量的字段,代码就会很冗长了。让我们使用`ReactLink`来减少打字输入: ~~~ var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return {message: 'Hello!'}; }, render: function() { return ; } }); ~~~ `LinkedStateMixin`给你的React组件添加一个叫做`linkState()`的方法。`linkState()`返回一个`ReactLink`对象,包含React state当前的值和一个用来改变它的回调函数。 `ReactLink`对象可以在树中作为props被向上传递或者向下传递,so it's easy (and explicit) to set up two-way binding between a component deep in the hierarchy and state that lives higher in the hierarchy. 注意,对于checkbox的`value`属性,有一个特殊的行为,如果checkbox被选中(默认是`on`),`value`属性值将会在表单提交的时候发送出去。当checkbox被选中或者取消选中的时候,`value`属性是不会更新的。对于checkbox,你应该使用`checkLink`而不是`valueLink`: ~~~ ~~~ ## 底层原理(Under the Hood) 对于`ReactLink`,有两块儿:你创建`ReactLink`实例的地方和你使用它的地方。为了证明`ReactLink`是多么的简单,让我们单独地重写每一块儿,以便显得更加明了。 ### 不带ReactLink的LinkedStateMixin(ReactLink Without LinkedStateMixin) ~~~ var WithoutMixin = React.createClass({ getInitialState: function() { return {message: 'Hello!'}; }, handleChange: function(newValue) { this.setState({message: newValue}); }, render: function() { var valueLink = { value: this.state.message, requestChange: this.handleChange }; return ; } }); ~~~ 如你所见,`ReactLink`对象是非常简单的,仅仅有一个`value`和`requestChange`属性。`LinkedStateMixin`也同样简单:它仅占据这些字段,用来自于`this.state`的值和一个调用`this.setState()`的回调函数。(And `LinkedStateMixin` is similarly simple: it just populates those fields with a value from `this.state` and a callback that calls`this.setState()`.) ### 不带valueLink的ReactLink(ReactLink Without valueLink) ~~~ var WithoutLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return {message: 'Hello!'}; }, render: function() { var valueLink = this.linkState('message'); var handleChange = function(e) { valueLink.requestChange(e.target.value); }; return ; } }); ~~~ `valueLink`属性也很简单。它简单地处理`onChange`事件,然后调用`this.props.valueLink.requestChange()`,同时也用`this.props.valueLink.value`替换`this.props.value`。就这么简单!
';

动画

最后更新于:2022-04-01 20:48:02

React为动画提供一个`ReactTransitonGroup`插件组件作为一个底层的API,一个`ReactCSSTransitionGroup`来简单地实现基本的CSS动画和过渡。 ## 高级API:`ReactCSSTransitionGroup` `ReactCSSTransitionGroup`是基于`ReactTransitionGroup`的,在React组件进入或者离开DOM的时候,它是一种简单地执行CSS过渡和动画的方式。这个的灵感来自于优秀的[ng-animate](http://www.nganimate.org/)库。 ### 快速开始 `ReactCSSTransitionGroup`是`ReactTransitions`的接口。这是一个简单的元素,包含了所有你对其动画感兴趣的组件。这里是一个例子,例子中我们让列表项淡入淡出。 ~~~ var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; var TodoList = React.createClass({ getInitialState: function() { return {items: ['hello', 'world', 'click', 'me']}; }, handleAdd: function() { var newItems = this.state.items.concat([prompt('Enter some text')]); this.setState({items: newItems}); }, handleRemove: function(i) { var newItems = this.state.items; newItems.splice(i, 1); this.setState({items: newItems}); }, render: function() { var items = this.state.items.map(function(item, i) { return (
{item}
); }.bind(this)); return (
{items}
); } }); ~~~ > 注意: > > 你必须为`ReactCSSTransitionGroup`的所有子级提供[`键`属性](http://reactjs.cn/react/docs/multiple-components.html#dynamic-children),即使只渲染一项。这就是React确定哪一个子级插入了,移除了,或者停留在那里。 在这个组件当中,当一个新的项被添加到`ReactCSSTransitionGroup`,它将会被添加`example-enter`类,然后在下一时刻被添加`example-enter-active` CSS类。这是一个基于`transitionName` prop的约定。 你可以使用这些类来触发一个CSS动画或者过渡。例如,尝试添加这段CSS代码,然后插入一个新的列表项: ~~~ .example-enter { opacity: 0.01; transition: opacity .5s ease-in; } .example-enter.example-enter-active { opacity: 1; } ~~~ 你将注意到,当你尝试移除一项的时候,`ReactCSSTransitionGroup`保持该项在DOM里。如果你正使用一个带有插件的未压缩的React构建版本,你将会看到一条警告:React期待一次动画或者过渡发生。那是因为`ReactCSSTransitionGroup`保持你的DOM元素一直在页面上,直到动画完成。尝试添加这段CSS代码: ~~~ .example-leave { opacity: 1; transition: opacity .5s ease-in; } .example-leave.example-leave-active { opacity: 0.01; } ~~~ ### 一组动画必须要挂载了才能生效 为了能够给它的子级应用过渡效果,`ReactCSSTransitionGroup`必须已经挂载到了DOM。下面的例子不会生效,因为`ReactCSSTransitionGroup`被挂载到新项,而不是新项被挂载到`ReactCSSTransitionGroup`里。将这个与上面的[快速开始](http://reactjs.cn/react/docs/animation.html#getting-started)部分比较一下,看看有什么差异。 ~~~ render: function() { var items = this.state.items.map(function(item, i) { return (
{item}
); }, this); return (
{items}
); } ~~~ ### 让一项或者零项动起来(Animating One or Zero Items) 虽然在上面的例子中,我们渲染了一个项目列表到`ReactCSSTransitionGroup`里,`ReactCSSTransitionGroup`的子级可以是一个或零个项目。这使它能够让一个元素实现进入和离开的动画。同样,你可以通过移动一个新的元素来替换当前元素。随着新元素的移入,当前元素移出。例如,我们可以由此实现一个简单的图片轮播器: ~~~ var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; var ImageCarousel = React.createClass({ propTypes: { imageSrc: React.PropTypes.string.isRequired }, render: function() { return (
); } }); ~~~ ### 禁用动画 如果你想,你可以禁用`入场`或者`出场`动画。例如,有些时候,你可能想要一个`入场`动画,不要`出场`动画,但是`ReactCSSTransitionGroup`会在移除DOM节点之前等待一个动画完成。你可以给`ReactCSSTransitionGroup`添加`transitionEnter={false}`或者`transitionLeave={false}` props来禁用这些动画。 > 注意: > > 当使用`ReactCSSTransitionGroup`的时候,没有办法通知你在过渡效果结束或者在执行动画的时候做一些复杂的运算。如果你想要更多细粒度的控制,你可以使用底层的`ReactTransitionGroup` API,该API提供了你自定义过渡效果所需要的函数。 ## 底层的API:`ReactTransitionGroup` `ReactTransitionGroup`是动画的基础。它可以通过`React.addons.TransitionGroup`得到。当子级被添加或者从其中移除(就像上面的例子)的时候,特殊的生命周期函数就会在它们上面被调用。 ### `componentWillEnter(callback)` 在组件被添加到已有的`TransitionGroup`中的时候,该函数和`componentDidMount()`被同时调用。这将会阻塞其它动画触发,直到`callback`被调用。该函数不会在`TransitionGroup`初始化渲染的时候调用。 ### `componentDidEnter()` 该函数在传给`componentWillEnter`的`callback`函数被调用之后调用。 ### `componentWillLeave(callback)` 该函数在子级从`ReactTransitionGroup`中移除的时候调用。虽然子级被移除了,`ReactTransitionGroup`将会使它继续在DOM中,直到`callback`被调用。 ### `componentDidLeave()` 该函数在`willLeave` `callback`被调用的时候调用(与`componentWillUnmount`是同一时间)。 ### 渲染一个不同的组件 默认情况下`ReactTransitionGroup`渲染一个`span`。你可以通过提供一个`component` prop来改变这种行为。例如,以下是你将如何渲染一个``: ~~~ ... ~~~ 每一个React能渲染的DOM组件都是可用的。但是,`组件`并不需要是一个DOM组件。它可以是任何你想要的React组件;甚至是你自己已经写好的! > 注意: > > v0.12之前,当使用DOM组件的时候,`组件` prop需要是一个指向`React.DOM.*`的引用。既然组件简单地传递到了`React.createElement`,它必须是一个字符串。组装的组件必须传递构造函数。 任何额外的、用户定义的属性将会成为已渲染的组件的属性。例如,以下是你将如何渲染一个带有css类的``: ~~~ ... ~~~
';

插件

最后更新于:2022-04-01 20:48:00

`React.addons` 是为了构建 React 应用而放置的一些有用工具的地方。**此功能应当被视为实验性的**,但最终将会被添加进核心代码中或者有用的工具库中: * [`TransitionGroup`和`CSSTransitionGroup`](http://reactjs.cn/react/docs/animation.html),用于处理动画和过渡,这些通常实现起来都不简单,例如在一个组件移除之前执行一段动画。 * [`LinkedStateMixin`](http://reactjs.cn/react/docs/two-way-binding-helpers.html),用于简化用户表单输入数据和组件 state 之间的双向数据绑定。 * [`classSet`](http://reactjs.cn/react/docs/class-name-manipulation.html),用于更加干净简洁地操作 DOM 中的 `class` 字符串。 * [`cloneWithProps`](http://reactjs.cn/react/docs/clone-with-props.html),用于实现 React 组件浅复制,同时改变它们的 props 。 * [`update`](http://reactjs.cn/react/docs/update.html),一个辅助方法,使得在 JavaScript 中处理不可变数据更加容易。 * [`PureRednerMixin`](http://reactjs.cn/react/docs/pure-render-mixin.html),在某些场景下的性能检测器。 以下插件只存在于 React 开发版(未压缩): * [`TestUtils`](http://reactjs.cn/react/docs/test-utils.html), 简单的辅助工具,用于编写测试用例(仅存在于未压缩版). * [`Perf`](http://reactjs.cn/react/docs/perf.html),用于性能测评,并帮助你检查出可优化的功能点。 要使用这些插件,需要用 `react-with-addons.js` (和它的最小化副本)替换常规的`React.js`。 当通过npm使用react包的时候,只要简单地用 `require('react/addons')` 替换`require('react')` 来得到带有所有插件的React。
';

工具集成(Tooling Integration)

最后更新于:2022-04-01 20:47:58

每个项目使用不同的系统来构建和部署JavaScript。我们尝试尽量让React环境无关。 ## React ### CDN托管的React 我们在我们的[下载页面](http://reactjs.cn/react/downloads.html)提供了React的CDN托管版本。这些预构建的文件使用UMD模块格式。直接简单地把它们放在``标签中将会给你环境的全局作用域引入一个`React`对象。React也可以在CommonJS和AMD环境下正常工作。 ### 使用主分支 我们在[GitHub仓库](https://github.com/facebook/react)的主分支上有一些构建指令。我们在`build/modules`下构建了符合CommonJS模块规范的树形目录,你可以放置在任何环境或者使用任何打包工具,只要支持CommonJS规范。 ## JSX ### 浏览器中的JSX转换 如果你喜欢使用JSX,我们[在我们的下载页面](http://reactjs.cn/react/downloads.html)提供了一个用于开发的浏览器中的JSX转换器。简单地用一个``标签来触发JSX转换器。 > 注意: > > 浏览器中的JSX转换器是相当大的,并且会在客户端导致无谓的计算,这些计算是可以避免的。不要在生产环境使用 - 参考下一节。 ### 生产环境化:预编译JSX 如果你有[npm](http://npmjs.org/),你可以简单地运行`npm install -g react-tools`来安装我们的命令行`jsx`工具。这个工具会把使用JSX语法的文件转换成纯的可以直接在浏览器里面运行起来的JavaScript文件。它也会为你监视目录,然后自动转换变化的文件;例如:`jsx --watch src/ build/`。运行`jsx --help`来查看更多关于如何使用这个工具的信息。 ### 有用的开源项目 开源社区开发了在几款编辑器中集成JSX的插件和构建系统。点击[JSX集成](https://github.com/facebook/react/wiki/Complementary-Tools#jsx-integrations)查看所有内容。
';

关于Refs的更多内容

最后更新于:2022-04-01 20:47:55

';

浏览器中的工作原理

最后更新于:2022-04-01 20:47:53

React提供了强大的抽象,让你在大多数应用场景中不再直接操作DOM,但是有时你需要简单地调用底层的API,或者借助于第三方库或已有的代码。 ## 虚拟DOM React是很快的,因为它从不直接操作DOM。React在内存中维护一个快速响应的DOM描述。`render()`方法返回一个DOM的_描述_,React能够利用内存中的描述来快速地计算出差异,然后更新浏览器中的DOM。 另外,React实现了一个完备的虚拟事件系统,尽管各个浏览器都有自己的怪异行为,React确保所有事件对象都符合W3C规范,并且持续冒泡,用一种高性能的方式跨浏览器(and everything bubbles consistently and in a performant way cross-browser)。你甚至可以在IE8中使用一些HTML5的事件! 大多数时候你应该呆在React的“虚拟浏览器”世界里面,因为它性能更加好,并且容易思考。但是,有时你简单地需要调用底层的API,或许借助于第三方的类似于jQuery插件这种库。React为你提供了直接使用底层DOM API的途径。 ## Refs和getDOMNode() 为了和浏览器交互,你将需要对DOM节点的引用。每一个挂载的React组件有一个`getDOMNode()`方法,你可以调用这个方法来获取对该节点的引用。 > 注意: > > `getDOMNode()`仅在挂载的组件上有效(也就是说,组件已经被放进了DOM中)。如果你尝试在一个未被挂载的组件上调用这个函数(例如在创建组件的`render()`函数中调用`getDOMNode()`),将会抛出异常。 为了获取一个到React组件的引用,你可以使用`this`来得到当前的React组件,或者你可以使用refs来指向一个你拥有的组件。它们像这样工作: ~~~ var MyComponent = React.createClass({ handleClick: function() { // Explicitly focus the text input using the raw DOM API. this.refs.myTextInput.getDOMNode().focus(); }, render: function() { // The ref attribute adds a reference to the component to // this.refs when the component is mounted. return (
); } }); React.render( , document.getElementById('example') ); ~~~ ## 更多关于Refs 为了学习更多有关Refs的内容,包括如何有效地使用它们,参考我们的[更多关于Refs](http://reactjs.cn/react/docs/more-about-refs.html)文档。 ## 组件生命周期 组件的生命周期包含三个主要部分: * **挂载:** 组件被插入到DOM中。 * **更新:** 组件被重新渲染,查明DOM是否应该刷新。 * **移除:** 组件从DOM中移除。 React提供生命周期方法,你可以在这些方法中放入自己的代码。我们提供**will**方法,会在某些行为发生之前调用,和**did**方法,会在某些行为发生之后调用。 ### 挂载 * `getInitialState(): object`在组件被挂载之前调用。状态化的组件应该实现这个方法,返回初始的state数据。 * `componentWillMount()`在挂载发生之前立即被调用。 * `componentDidMount()`在挂载结束之后马上被调用。需要DOM节点的初始化操作应该放在这里。 ### 更新 * `componentWillReceiveProps(object nextProps)`当一个挂载的组件接收到新的props的时候被调用。该方法应该用于比较`this.props`和`nextProps`,然后使用`this.setState()`来改变state。 * `shouldComponentUpdate(object nextProps, object nextState): boolean`当组件做出是否要更新DOM的决定的时候被调用。实现该函数,优化`this.props`和`nextProps`,以及`this.state`和`nextState`的比较,如果不需要React更新DOM,则返回false。 * `componentWillUpdate(object nextProps, object nextState)`在更新发生之前被调用。你可以在这里调用`this.setState()`。 * `componentDidUpdate(object prevProps, object prevState)`在更新发生之后调用。 ### 移除 * `componentWillUnmount()`在组件移除和销毁之前被调用。清理工作应该放在这里。 ### 挂载的方法(Mounted Methods) _挂载的_复合组件也支持如下方法: * `getDOMNode(): DOMElement`可以在任何挂载的组件上面调用,用于获取一个指向它的渲染DOM节点的引用。 * `forceUpdate()`当你知道一些很深的组件state已经改变了的时候,可以在该组件上面调用,而不是使用`this.setState()`。 ## 跨浏览器支持和兼容代码(Browser Support and Polyfills) 在Facebook,我们支持低版本的浏览器,包括IE8。我们已经写好兼容代码很长时间了,这能让我们写有远见的JS。这意味着我们没有零散的骇客代码充斥在我们的代码库里面,并且我们依然能够预计我们的代码“正常工作起来”。例如,不使用`+new Date()`,我们能够写`Date.now()`。 At Facebook, we support older browsers, including IE8\. We've had polyfills in place for a long time to allow us to write forward-thinking JS. This means we don't have a bunch of hacks scattered throughout our codebase and we can still expect our code to "just work". For example, instead of seeing `+new Date()`, we can just write `Date.now()`. Since the open source React is the same as what we use internally, we've carried over this philosophy of using forward thinking JS. In addition to that philosophy, we've also taken the stance that we, as authors of a JS library, should not be shipping polyfills as a part of our library. If every library did this, there's a good chance you'd be sending down the same polyfill multiple times, which could be a sizable chunk of dead code. If your product needs to support older browsers, chances are you're already using something like [es5-shim](https://github.com/kriskowal/es5-shim). ### 支持低版本浏览器的兼容代码 [kriskowal的es5-shim](https://github.com/kriskowal/es5-shim) `es5-shim.js` 提供了以下react需要的api: * `Array.isArray` * `Array.prototype.every` * `Array.prototype.forEach` * `Array.prototype.indexOf` * `Array.prototype.map` * `Date.now` * `Function.prototype.bind` * `Object.keys` * `String.prototype.split` * `String.prototype.trim` [kriskowal的es5-shim](https://github.com/kriskowal/es5-shim) `es5-sham.js` 同样提供了以下react需要的api: * `Object.create` * `Object.freeze` The unminified build of React needs the following from [paulmillr's console-polyfill](https://github.com/paulmillr/console-polyfill). * `console.*` When using HTML5 elements in IE8 including ``, ``, ``, ``, and ``, it's also necessary to include [html5shiv](https://github.com/aFarkas/html5shiv) or a similar script. ### Cross-browser Issues Although React is pretty good at abstracting browser differences, some browsers are limited or present quirky behaviors that we couldn't find a workaround for. #### onScroll event on IE8 On IE8 the `onScroll` event doesn't bubble and IE8 doesn't have an API to define handlers to the capturing phase of an event, meaning there is no way for React to listen to these events. Currently a handler to this event is ignored on IE8. See the [onScroll doesn't work in IE8](https://github.com/facebook/react/issues/631) GitHub issue for more information. ve carried over this philosophy of using forward thinking JS. In addition to that philosophy, we've also taken the stance that we, as authors of a JS library, should not be shipping polyfills as a part of our library. If every library did this, there's a good chance you'd be sending down the same polyfill multiple times, which could be a sizable chunk of dead code. If your product needs to support older browsers, chances are you're already using something like [es5-shim](https://github.com/kriskowal/es5-shim). ### Polyfills Needed to Support Older Browsers `es5-shim.js` from [kriskowal's es5-shim](https://github.com/kriskowal/es5-shim) provides the following that React needs: * `Array.isArray` * `Array.prototype.every` * `Array.prototype.forEach` * `Array.prototype.indexOf` * `Array.prototype.map` * `Date.now` * `Function.prototype.bind` * `Object.keys` * `String.prototype.split` * `String.prototype.trim` `es5-sham.js`, also from [kriskowal's es5-shim](https://github.com/kriskowal/es5-shim), provides the following that React needs: * `Object.create` * `Object.freeze` The unminified build of React needs the following from [paulmillr's console-polyfill](https://github.com/paulmillr/console-polyfill). * `console.*` When using HTML5 elements in IE8 including ``, ``, ``, ``, and ``, it's also necessary to include [html5shiv](https://github.com/aFarkas/html5shiv) or a similar script. ### Cross-browser Issues Although React is pretty good at abstracting browser differences, some browsers are limited or present quirky behaviors that we couldn't find a workaround for. #### onScroll event on IE8 On IE8 the `onScroll` event doesn't bubble and IE8 doesn't have an API to define handlers to the capturing phase of an event, meaning there is no way for React to listen to these events. Currently a handler to this event is ignored on IE8. See the [onScroll doesn't work in IE8](https://github.com/facebook/react/issues/631) GitHub issue for more information.
';

表单组件

最后更新于:2022-04-01 20:47:51

诸如 ``、` ~~~ 对 HTML 而言,让开发者设置多行的值很容易。但是,React 是 JavaScript,没有字符限制,可以使用 `\n` 实现换行。简言之,React 已经有 `value`、`defaultValue` 属性,`` 组件的子节点扮演什么角色就有点模棱两可了。基于此, 设置`