这是一个React组件实现组件可交互所需的流程,render()输出虚拟DOM,虚拟DOM转为DOM,再在DOM上注册事件,事件触发setState()修改数据,在每次调用setState方法时,React会自动执行render方法来更新虚拟DOM,如果组件已经被渲染,那么还会更新到DOM中去。
这些是React目前支持的事件列表。
React的组件拥有一套清晰完整而且非常容易理解的生命周期机制,大体可以分为三个过程:初始化、更新和销毁,在组件生命周期中,随着组件的props或者state发生改变,它的虚拟DOM和DOM表现也将有相应的变化。
首先是初始化过程,这里会着重讲,需要充分理解。
组件类在声明时,会先调用 getDefaultProps() 方法来获取默认props值,这个方法会且只会在声明组件类时调用一次,这一点需要注意,它返回的默认props由所有实例共享。
在组件被实例化之前,会先调用一次实例方法 getInitialState() 方法,用于获取这个组件的初始state。
实例化之后就是渲染,componentWillMount方法会在生成虚拟DOM之前被调用,你可以在这里对组件的渲染做一些准备工作,比如计算目标容器尺寸然后修改组件自身的尺寸以适应目标容器等等。
接下来就是渲染工作,在这里你会创建一个虚拟DOM用来表示组件的结构。对于一个组件来说,render 是唯一一个必须的方法。render方法需要满足这几点:
不能对 props、state 或 DOM 进行修改
需要注意的是,render 方法返回的是虚拟DOM。
渲染完成以后,我们可能需要对DOM做一些操作,比如截屏、上报日志、或者初始化iScroll等第三方非React插件,可以在 componentDidMount() 方法中做这些事情。当然,你也可以在这个方法里通过 this.getDOMNode() 方法取得最终生成DOM节点,然后对DOM节点做爱做的事情,但需要注意做好安全措施,不要缓存已经生成的DOM节点,因为这些DOM节点随时可能被替换掉,所以应该在每次用的时候去读取。
组件被初始化完成后,它的状态会随着用户的操作、时间的推移、数据更新而产生变化,变化的过程是组件声明周期的另一部分 ——
更新过程。
当组件已经被实例化后,使用者调用 setProps() 方法修改组件的数据时,组件的 componentWillReceiveProps() 方法会被调用,在这里,你可以对外部传入的数据进行一些预处理,比如从props中读取数据写入state。
默认情况下,组件在 setState() 之后,React会遍历这个组件的所有子组件,进行“灌水”,将props从上到下一层一层传下去,并逐个执行更新操作,虽然React内部已经进行过很多的优化,这个过程并不会花费多少时间,但是程序员里永远不缺乏长期性能饥渴的同学,不用担心,React有一个能够解决你性能饥渴的办法——shouldComponentUpdate()——有时候,props发生了变化,但组件和子组件并不会因为这个props的变化而发生变化,打个比方,你有一个表单组件,你想要修改表单的name,同时你能够确信这个name不会对组件的渲染产生任何影响,那么你可以直接在这个方法里return false来终止后续行为。这样就能够避免无效的虚拟DOM对比了,对性能会有明显提升。
如果这个时候有同学仍然饥渴难耐,那么你可以尝试不可变数据结构(用过mongodb的同学应该懂)。
组件在更新前,React会执行componentWillUpdate() 方法,这个方法类似于前面看到的 componentWillMount()方法,唯一不同的地方只是这个方法在执行的时候组件是已经渲染过的。需要注意的是,不可以在这个方法中修改props或state,如果要修改,应当在 componentWillReceiveProps() 中修改。
然后是渲染,React会拿这次返回的虚拟DOM和缓存中的虚拟DOM进行对比,找出【最小修改点】,然后替换。
更新完成后,React会调用组件的componentDidUpdate 方法,这个方法类似于前面 componentDidMount 方法,你仍然可以在这里可以通过 this.getDOMNode() 方法取得最终的DOM节点。
香港电影结尾经常看到一个剧情,就是英雄打败了坏人,然后警察出来擦屁股——
componentWillUnmount 除了擦屁股什么也做不了。
你可以在这个方法中销毁非React组件注册的事件、插入的节点,或者一些定时器之类。这个过程可能容易出错,比如绑定了事件却没销毁,这个React可帮不了你,你自己约的炮,含着泪也要打完。
下面我们来看看React怎样结合nodejs实现服务端渲染。
服务端渲染有多快我就不多说了。
因为有虚拟DOM的存在,React可以很容易的将虚拟DOM转换为字符串,这便使我们可以只写一份UI代码,同时运行在node里和和浏览器里。
var html = React.renderToString(elem);
在node里将react组件HTML渲染为HTML,一句代码即可。
不过围绕这个renderToString我们还要做一些准备工作:
最后发送HTML和数据给浏览器
代码就不贴了,大家自行脑补。
需要注意的是这里的JSON字符串中可能出现</script>结尾标签或HTML注释,可能会导致语法错误,这里需要进行转义。
页面的示例代码本来打算用大家更熟悉的HTML,但发现代码量太多了,所以换成了jade代码,没用过jade的同学也顺便了解一下,我也顺便给jade打个广告。 这个页面做了这几件事:
渲染组件
这就是React的服务端渲染,组件的代码前后端都可以复用。
是不是感觉React挺牛逼的?还没完!
React能够用一套代码同时运行在浏览器和node里,而且能够以原生App的姿势运行在iOS和Android系统中,即拥有了web迭代迅速的特性,又拥有原生App的体验。
这个姿势叫做 React-Native。