先来回顾 React 的生命周期, 用流程图表示如下:
该流程图比较清晰地呈现了 react 的生命周期。其分为 3 个阶段 —— 生成期, 存在期, 销毁期。
因为生命周期钩子函数存在于自定义组件中, 将之前 _render 函数作些调整如下:
// 原来的 _render 函数, 为了将职责拆分得更细, 将 virtual dom 转为 real dom 的函数单独抽离出来function vdomToDom(vdom) {if (_.isFunction(vdom.nodeName)) { // 为了更加方便地书写生命周期逻辑, 将解析自定义组件逻辑和一般 html 标签的逻辑分离开const component = createComponent(vdom) // 构造组件setProps(component) // 更改组件 propsrenderComponent(component) // 渲染组件, 将 dom 节点赋值到 componentreturn component.base // 返回真实 dom}...}
我们可以在 setProps 函数内(渲染前)加入 componentWillMount
, componentWillReceiveProps
方法, setProps 函数如下:
function setProps(component) {if (component && component.componentWillMount) {component.componentWillMount()} else if (component.base && component.componentWillReceiveProps) {component.componentWillReceiveProps(component.props) // 后面待实现}}
而后我们在 renderComponent 函数内加入 componentDidMount
、shouldComponentUpdate
、componentWillUpdate
、componentDidUpdate
方法
function renderComponent(component) {if (component.base && component.shouldComponentUpdate) {const bool = component.shouldComponentUpdate(component.props, component.state)if (!bool && bool !== undefined) {return false // shouldComponentUpdate() 返回 false, 则生命周期终止}}if (component.base && component.componentWillUpdate) {component.componentWillUpdate()}const rendered = component.render()const base = vdomToDom(rendered)if (component.base && component.componentDidUpdate) {component.componentDidUpdate()} else if (component && component.componentDidMount) {component.componentDidMount()}if (component.base && component.base.parentNode) { // setState 进入此逻辑component.base.parentNode.replaceChild(base, component.base)}component.base = base // 标志符}
测试如下用例:
class A extends Component {componentWillReceiveProps(props) {console.log('componentWillReceiveProps')}render() {return (<div>{this.props.count}</div>)}}class B extends Component {constructor(props) {super(props)this.state = {count: 1}}componentWillMount() {console.log('componentWillMount')}componentDidMount() {console.log('componentDidMount')}shouldComponentUpdate(nextProps, nextState) {console.log('shouldComponentUpdate', nextProps, nextState)return true}componentWillUpdate() {console.log('componentWillUpdate')}componentDidUpdate() {console.log('componentDidUpdate')}click() {this.setState({count: ++this.state.count})}render() {console.log('render')return (<div><button onClick={this.click.bind(this)}>Click Me!</button><A count={this.state.count} /></div>)}}ReactDOM.render(<B />,document.getElementById('root'))
页面加载时输出结果如下:
componentWillMountrendercomponentDidMount
点击按钮时输出结果如下:
shouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate
在这个版本中, 新加入了两个生命周期:
getDerivedStateFromProps(nextProps, prevState): 更加语义化, 可以代替 componentWillMount、componentWillReceiveProps(nextProps);getSnapshotBeforeUpdate(prevProps, prevState): 可以将结果传入 componentDidUpdate 里, 从而达到 dom 数据统一, 可以替代 componentWillUpdate()(缺点就是前面讲的 react 开启异步渲染, componentWillUpdate() 与 componentDidUpdate() 间获取的 dom 会不统一)。
后文考虑实现上述 api。
React 16.3 生命周期相关文献