父子组件生命周期:
“生命周期”细想之下有点浪漫主义色彩,不知道是不是从lifecycle英译过来的。作为一个前端从业者,如果让我来取,可能会取成“渲染周期”之类的,毕竟是和浏览器打交道的职业,浏览器的layout使dom树具有骨架,paint则让整个页面光亮起来。
React 的一切都是组件,通过 React.createElement 方法来创建嵌套层级,说白了在内存中构建对象树,据此渲染到浏览器中成为dom树,这个时候一个节点是什么时候真正渲染到页面中就变得重要起来,因为只有这个时候你才能真正和浏览器环境内的对象和方法交互,同样离开的时候也需要清理监听器等防止干扰后续逻辑,因此钩子函数,也可以说是生命周期函数就有了存在的意义。
先上代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <script crossorigin src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> </head> <body> <div id="app"></div> <div id="hollow"></div> <script type="text/babel"> const { Component, Fragment } = React; class F extends Component { state = { x: ‘state before‘ }; static getDerivedStateFromProps(getProps, getState) { console.log(‘F get props‘, { getProps, getState }); } componentDidMount() { console.log(‘F did mount‘); setTimeout(() => { console.log(‘%c %s‘, ‘color:blue‘, ‘start to update state‘); this.setState({ x: ‘state after‘ }); }, 2000); } shouldComponentUpdate(nextProps, nextState) { console.log(‘F should update‘, { nextProps, nextState }); return true; } componentDidUpdate(prevProps, prevState) { console.log(‘F did update‘, { prevProps, prevState }); } componentWillUnmount() { console.log(‘F will unmount‘, Date.now()); } render() { return ( <div> {this.props.x} {this.state.x} </div> ); } } class App extends Component { state = { x: ‘props before ‘ }; componentDidMount() { console.log(‘App did mount‘); setTimeout(() => { console.log(‘%c %s‘, ‘color:red‘, ‘start to update props‘); this.setState({ x: ‘props after ‘ }); }, 2000); } componentWillUnmount() { console.log(‘App will unmount‘, Date.now()); } render() { return <F {...this.state} />; } } setTimeout(() => { ReactDOM.render(‘unmount Component App at ‘ + Date.now(), app); }, 6000); ReactDOM.render(<App />, app); </script> </body> </html>
Props 和 State 相关
父组件 App 将自身的State传入了子组件 F 内,忽略挂载和卸载,列举生命周期函数:
1,getDerivedStateFromProps --
通过浏览器打印结果可以看到,不管是组件初始化,还是更新或继承新的 state 或者 props ,最先触发的都是静态钩子函数 getDerivedStateFromProps ;
2,shouldComponentUpdate --
在 setState 异步赋值了 state 的下一个状态后,整个子组件 F 开始收集和对比新旧状态,将新的状态输入到生命周期函数 shouldComponentUpdate 中,写明 return 的值是 truthy 还是 falsy 可以选择中断更新或者继续更新;
3,componentDidUpdate --
在 shouldComponentUpdate 顺利进入下一步后,将执行 render 方法,更新虚拟dom树,浏览器完成渲染,在此之后旧的状态将作为参数传给 componentDidUpdate ,可以对比新状态以及是否保留旧状态;
挂载 和 卸载
componentDidMount 和 componentWillUnmount --
观察父子组件的挂载生命周期函数,可以发现挂载时,子组件的挂载钩子先被触发;卸载时,子组件的卸载钩子后被触发;
对于挂载钩子,一般来说,应该将子组件从上至下依次挂载到一个 fragment 上,再整体挂载到dom树中,因为频繁操作dom树不仅影响性能甚至可能影响用户体验。
但是实际情况却并如此,我们经常在挂载函数上注册监听器,说明此时是可以与页面交互的,也就是说其实所有挂载钩子都是在父组件实际挂载到dom树上才触发的,不过是在父组件挂载后依次触发子组件的 componentDidmount ,最后再触发自身的挂载钩子,说白了,componentDidMount 其实是异步钩子。
相反,卸载的时候父节点先被移除,再从上至下依次触发子组件的卸载钩子;
但是我们也经常在卸载钩子上卸载监听器,这说明 componentWillUnmount 其实在父组件从dom树上卸载前触发的,先触发自身的卸载钩子,但此时并未从dom树上剥离,然后依次尝试触发所有子组件的卸载钩子,最后,父组件从dom树上完成实际卸载。
原文地址:https://www.cnblogs.com/lowki/p/11317088.html