一、react
react生命周期
包括:数据初始化、创建、挂载、更新、销毁
react生命周期在项目中的执行顺序
constructor() => componentWillMount() => render() => componentDidMount()
当更新执行直接执行以下:
componentWillReceiveProps (nextProps) => shouldComponentUpdate(nextProps,nextState) => componentWillUpdate (nextProps,nextState) => render() => componentDidUpdate(prevProps,prevState)
关闭页面组件最终都会销毁: componentWillUnmount ()
react新增的生命周期:
-
getDerivedStateFromProps(nextProps, prevState)
-
getSnapshotBeforeUpdate(prevProps, prevState)
强调几点:
-
constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
注:只要使用了constructor()就必须写super(),否则会导致this指向错误
。 -
render()中会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
getDerivedStateFromProps()代替componentWillReceiveProps()
在 componentWillReceiveProps 中,一般会做以下两件事:
- 根据 props 来更新 state
- 触发一些回调,如动画或页面跳转等。
componentWillReceiveProps缺点:
- 会破坏 state 数据的单一数据源,导致组件状态变得不可预测
- 也会增加组件的重绘次数
- 更新state和触发回调都在componentWillReceiveProps中执行
getDerivedStateFromProps优点:
-
getDerivedStateFromProps 中禁止了组件去访问 this.props,
强制让开发者去比较 nextProps 与 prevState 中的值
,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去做其他一些让组件自身状态变得更加不可预测的事情。 -
更新 state在getDerivedStateFromProps中,触发回调在componentDidUpdate中,使得组件整体的更新逻辑更为清晰
// before
componentWillReceiveProps(nextProps) {
if (nextProps.isSuccess !== this.props.isSuccess) {
this.setState({
isLogin: nextProps.isSuccess,
});
}
if (nextProps.isSuccess) {
this.close();
}
}
// after
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.isSuccess !== prevState.isSuccess) {
return {
isLogin: nextProps.isSuccess,
};
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if (!prevState.isSuccess && this.props.isSuccess) {
this.close();
}
}
getSnapshotBeforeUpdate()代替componentWillUpdate()
componentWillUpdate的常见用例
:读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理
componentWillUpdate的缺点:
- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了
。
getSnapshotBeforeUpdate的优点:
getSnapshotBeforeUpdate 会在最终的 render 之前被调用
,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。- 此生命周期返回的任何值都将作为参数传递给componentDidUpdate()
二、vue
vue生命周期
vue每个组件都是独立的,每个组件都有一个属于它的生命周期。
包括:数据初始化、创建、挂载、更新、销毁
vue生命周期在项目中的执行顺序
beforeCeate() => data() => created() => beforeMount() => mounted()
当更新会再created之后插入: beforeUpdate => updated
关闭页面组件最终都会销毁: beforeDestroy() => destroyed()
强调几点:
- beforeCeate在事件和生命周期钩子初始化前调用
- data的初始化是在created前完成数据观测(data observer)
https://github.com/vuejs/vue/blob/dev/src/core/instance/init.js
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
vue中内置方法属性的运行顺序(methods、computed、data、watch、props)
从源码可以知道: props => methods =>data => computed => watch
https://github.com/vuejs/vue/blob/dev/src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
几点注意
- 异步请求最好在mounted中执行
$refs
获取dom元素,在$nextTick
中执行$refs
直接访问子组件的方法,可能有数据的延迟滞后的bug,可以采用异步回调的方式解决
handleAsync () {
return new Promise(resolve=>{
resolve(res)
})
}
async handleShow() {
await this.handleAsync().then(res=>{
this.$refs.child.fun(res);
})
}
比较
相同点:
- react和vue异步请求都最好在挂载函数(componentDidMount和mounted)中执行
- 生命周期都包含:初始化、创建、挂载、销毁、更新
- 都是通过refs获取dom元素
- 都需要进行卸载和数据的销毁:setTimeout、setInterval、removeEventListener等
不同点:
- 更新过程挂载阶段生命周期不一样,react用新函数componentDidUpdate,vue还是用mounted
- 写法大不一样