Fiber 架构
Reconciler(协调):跟踪组件的状态变化,并将更新的状态映射到到新的界面。React 的元素、生命周期、 render
方法,以及应用于组件子元素的 diffing 算法综合起到的作用
Stack Reconciler
V16- 中更新过程是同步的,React 挂载或者更新的时候,递归调用各个组件生命周期,对比VDOM 更新 DOM 树,整个过程一旦开始,不可被打断。一个调用链很长并且计算量很大的任务的调用栈。
浏览器主线程被渲染过程占用比较长的时间,导致进程阻塞
渲染过程没有优先级可言,不同状态变化是需要有优先级的
Fiber Reconciler
Fiber 把渲染过程分成了一个个 chunk,变同步为异步,提升性能:
增量渲染:将渲染任务拆分成块,匀到多帧
渲染过程中有机会将主线程交给 react 以外的 JS 调用(如用户操作)
渲染有了优先级,能让高优先级的工作先做
Fiber 之后,将更新分了两个部分
render/reconciliation(可中断)
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
commit(不可中断)
componentDidMount
componentDidUpdate
componentWillUnmount
新增了一个顶级 API: ReactDOM.createPortal
Portal 本身不是新的概念,在以往使用 React 实现模态框等组件时我们也会用到,现在 React 官方提供了 API 使得能够更方便地实现这样的功能。
在一般的 React 结构中,组件的嵌套关系和渲染出来的 DOM 的嵌套关系是一致的(子组件渲染出的 DOM 一定是在父组件渲染出的 DOM 的内部的)。
但某些情况下,这样的限制会导致问题,例如实现一个模态框(Modal),虽然模态框所在的组件在它的父组件内部,但是通常需要被渲染在
body
元素下。 新的 API 使用方式如下:
render() {
// React 会在你提供的 domNode 下渲染,而不是在当前组件所在的 DOM
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
更多信息可以查看 Portals
已改变的生命周期
即将移除的
componentWillMount:
UNSAFE_componentWillMount
componentWillReceiveProps:
UNSAFE_componentWillReceiveProps
componentWillUpdate:
UNSAFE_componentWillUpdate
新增的
在发布Error Boundaries
的时候,React提供了一个新的生命周期方法componentDidCatch
,在捕获到错误的时候会触发,你可以在里面修改state
以显示错误提醒的UI,或者将错误信息发送给服务端进行log
用于后期分析。但是这里有个问题,就是在捕获到错误的瞬间,React会在这次渲染周期中将这个组件渲染为null
,这就有可能导致他的父组件设置他上面的ref
获得null
而导致一些问题,所以现在提供了这个方法。
这个方法跟getDerivedStateFromProps
类似,唯一的区别是他只有在出现错误的时候才触发,他相对于componentDidCatch
的优势是在当前的渲染周期中就可以修改state
,以在当前渲染就可以出现错误的UI,而不需要一个null
的中间态。
而这个方法的出现,也意味着以后出现错误的时候,修改state
应该放在这里去做,而后续收集错误信息之类的放到componentDidCatch
里面。
更好的错误处理方式
V16-:handleError(error)
unstable_handleError: function(error){
this.setState({error: "oops"})
}
只能处理 render() 抛出 Error,其他的生命周期函数触发不了
能捕获当前组件本身抛出的 Error,但是还是会阻碍接下来的渲染
V 16+:componentDidCatch(error, info )
error: 捕获的错误
info: 错误信息栈
// info
componentStack: "
in BuggyCounter (created by App)
in ErrorBoundary (created by App)
in div (created by App)
in App"
使用方法
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
componentDidCatch(err, info) {
this.setState({ hasError: false })
// 上报错误
logErrorToMyService(err, info)
}
render() {
if (this.state.hasError) {
return <h1>Something is wrong</h1>
}
return this.props.children
}
}
<ErrorBoundary>
<otherComponent></otherComponent>
<ErrorBoundary>
ErrorBoundary 作为一个专门处理错误的异常边界组件
能捕获所有子组件,任何生命周期函数抛出的异常
只能捕获子组件抛出的 error,组件本身的异常无法捕获。因此我们的异常边界组件不能涉及业务代码,否则将无法捕获自身逻辑错误
子组件抛出的错误会层层往上传递,直到被捕获,一到捕获之后,就不会再往上传播。
注意:
Error boundaries
不能捕获的错误事件处理
异步
(e.g.setTimeout or requestAnimationFrame callbacks)
服务端渲染
组件自身的错误
错误处理表现
在 React16 之后,未被捕获的错误,错误会一直往上抛,直到 React 将 React Tree 全部卸载。同时,在开发环境,控制台会输出所有的错误,包括被捕获的错误。
新的 Render 返回
React elements
Booleans OR null
V16.0.0 Arrays and V16.2.0 Fragments
render() {
// 不需要再包裹一层了!
return [
// 但是要使用 key
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
render() {
return 'Look ma, no spans!';
}
更小的体积
- V16-,
react
+react-dom
在 gizp 后的总体积为109KB
,V16 比之前版本的161.7KB
减小了30%
。
部分非核心模块被移出
React.createClass
、React.PropTypes
等模块被移出了react
包,现在你必须从单独的包里引入。
基于 MIT 协议
- 现在 React 15.6.2 和 React 16+ 都是基于 MIT 协议了,你可以自由选择
lazy
React 16.6
配合发布了一个 Suspense
组件,也就是最开始命名为 Placeholder
的组件,用来配合 render
方法内部的异步操作的,让我们先来看一下 lazy
的用法
import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
);
}
然而这并不仅仅 Suspense
的唯一用处,React正式开放 Suspense
组件代表着所有异步的操作都可以在 render
方法里面做了Suspense
能够让加载数据的操作变得异常简单。Dan 在冰岛的分享,现在npm上也有一个包提供数据加载的功能了,simple-cache-provider,但是现在还不要在正式环境使用他哦。
React.memo
ClassComponent
可以通过继承类PureComponent
或者实现shouldComponentUpdate
来主动判断组件是否需要重新渲染,以此来提高性能,但是FunctionalComponent
到目前为止没有类似的功能。
所以今天React发布了React.memo
方法,来实现类似PureComponent
的功能,即浅比较props
是否有变化,如果没有变化,就不重新渲染当前组件
const FunctionalComponent = React.memo((props) => {
})
参考资源
Lin Clark 的演讲视频React Fiber是什么完全理解React Fiber
Inside Fiber: in-depth overview of the new reconciliation algorithm in React