# React 16
包含的更新:
- fragments 返回片段代码类型
- error boundaries 更好的错误处理/异常边界
- portals 挂载方式
- custom DOM attributes 自定义的DOM属性
- server-side rendering 提升服务器端渲染性能
- reduced file size 更小的文件体积
# render函数可返回新的类型:数组和字符串
新的版本支持组件的render方法返回包含元素的数组类型,代码如下:
render() {
//不需要再把所有的元素绑定到一个单独的元素中了
return [
// 别忘记加上key值
<li key="A"/>First item</li>,
<li key="B"/>Second item</li>,
<li key="C"/>Third item</li>,
];
}
新的版本也支持render方法返回的类型为strings
# 更好的处理错误
之前的React,渲染过程中如果遇到运行时的错误,可能会导致整个React组件的崩溃,并产生一些隐藏的错误信息,需要重新刷新才能恢复。为了解决这个问题,React16 使用了一个更有弹性的错误处理策略。默认情况下,如果一个错误是在组件的渲染或者生命周期方法中被抛出,整个组件结构就会从根节点中卸载。这种方式阻碍了被坏数据的展示,然而,却不是很好的用户体验。
新的策略,对于这种一出现错误就卸载整个组件app的方式进行了改善,你可以使用error boundaries(错误边缘)来进行处理,它是专门用来抓取其下子组件错误并向视图展示错误信息的组件。将error boundaries(错误边缘)想象成try{}catch(){}语句,只是它是React专用而已。
关于这个特性的说明,详见Error Handling in React (opens new window)
详见代码:
# 挂载方式
Portals(挂载方式)提供了一个非常好的方式,可以将渲染的children插入到一个DOM节点,而这个节点可以是存在于当前组件dom结构外的其他节点。
render() {
// 可以不必创建一个新的div标签,divNode是一个存在于dom结构的节点,不需要考虑这个节点的位置
return React.createPortal(
this.props.children,
divNode,
);
}
# 更好的服务端渲染
React16更好的支持服务端html的渲染,不再需要服务端进行初始化渲染以匹配结果了,它会尝试重新利用尽可能多的已经存在的DOM节点。
服务端渲染器被完全重写用以支持流。React的核心成员Sasha Aicken,它是这个功能的贡献者,写了一篇非常好的文章 (opens new window)来描述React16 SSR的提升。“渲染流的方式能够减少获取响应首字节前所花费的毫秒数(TTFB),将页面文档的开头沿着电缆发送到浏览器端时,下一部分的页面文档已经形成了。用这种流的方式,所有的主流浏览器都会更早的开始解析和渲染页面文档”。
# 支持自定义的DOM属性
取代之前忽略不识别的HTML和SVG属性的方式,新的版本将会把它们传递给DOM元素。这个新特性会让我们摆脱可用的React属性白名单,从而减少文件的大小。
# 减少文件体积
除了上面提交到的这些特性,React16要比v15.6.1更小!
react 大小从之前的20.7kb(压缩后6.9kb)降低到现在的5.3kb (压缩后2.2kb)。 react-dom 从之前的141kb(压缩后42.9kb)降低到现在的103.7kb(压缩后32.6kb)。 react + react-dom 从之前的161.7kb(压缩后49.8kb)到现在的109kb(压缩后43.8kb)。 文件大小的变化部分归功于打包工具的改变。React现在使用Rollup针对不同的目标格式创建打包,进而使文件大小和运行时性能都表现优秀。
# 新的核心架构
React16是React第一个建立在一个称为"Fiber"全新架构的版本。你可以通过阅读Facebook的工程博客了解Fiber全部的内容。
这个版本中的大部分功能,比如error boundaries和fragments,这都是通过重写和核心架构来实现这些可能的。而事后的几个版本中,你能够回去更多的新特性,因为我们会开始全力释放React的全部潜能。
也许最让人兴奋的地方是我们正致力于async rendering(异步渲染)的工作 —— 这个策略能通过周期性的向浏览器发布执行任务从而协同调度渲染工作。结果就是使用异步渲染,应用将会更加响应式,因为React避免了主线程的阻塞。
我们认为异步渲染是一个充满意义的功能,它代表了React的未来。为了能够保证现有项目平滑的融合v16.0版本,这个版本我们并没有启动任何异步的特性,但是在接下来的几个月里我们会非常高兴的请它隆重登场。请大家拭目以待!
对于如何区别优先级,React 有自己的一套逻辑。对于动画这种实时性很高的东西,也就是 16 ms 必须渲染一次保证不卡顿的情况下,React 会每 16 ms(以内) 暂停一下更新,返回来继续渲染动画。
对于异步渲染,现在渲染有两个阶段:reconciliation
和 commit
。前者过程是可以打断的,后者不能暂停,会一直更新界面直到完成。
Reconciliation 阶段
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
Commit 阶段
componentDidMount
componentDidUpdate
componentWillUnmount
因为 reconciliation 阶段是可以被打断的,所以 reconciliation 阶段会执行的生命周期函数就可能会出现调用多次的情况,从而引起 Bug。所以对于 reconciliation 阶段调用的几个函数,除了 shouldComponentUpdate 以外,其他都应该避免去使用,并且 V16 中也引入了新的 API 来解决这个问题。
getDerivedStateFromProps 用于替换 componentWillReceiveProps ,该函数会在初始化和 update 时被调用 getSnapshotBeforeUpdate 用于替换 componentWillUpdate ,该函数会在 update 后 DOM 更新前被调用,用于读取最新的 DOM 数据
class ExampleComponent extends React.Component {
// 用于初始化 state
constructor() {}
// 用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用
// 因为该函数是静态函数,所以取不到 `this`
// 如果需要对比 `prevProps` 需要单独在 `state` 中维护
static getDerivedStateFromProps(nextProps, prevState) {}
// 判断是否需要更新组件,多用于组件性能优化
shouldComponentUpdate(nextProps, nextState) {}
// 组件挂载后调用
// 可以在该函数中进行请求或者订阅
componentDidMount() {}
// 用于获得最新的 DOM 数据
getSnapshotBeforeUpdate() {}
// 组件即将销毁
// 可以在此处移除订阅,定时器等等
componentWillUnmount() {}
// 组件销毁后调用
componentDidUnMount() {}
// 组件更新后调用
componentDidUpdate() {}
// 渲染组件函数
render() {}
// 以下函数不建议使用
UNSAFE_componentWillMount() {}
UNSAFE_componentWillUpdate(nextProps, nextState) {}
UNSAFE_componentWillReceiveProps(nextProps) {}
}
# 参考文档
- 官方的文档:https://reactjs.org/blog/2017/09/26/react-v16.0.html
- preview文档:https://deploy-preview-10824--reactjs.netlify.com/blog/2017/09/26/react-v16.0.html
- segmentfault: https://segmentfault.com/a/1190000011653365