学习 react 的生命周期的时候认为会 vue 差不多, 后果发现还是有差异的, vue3 的生命周期则和 vue2 没什么变动,
这次文章比拟长, 比拟偏重父子生命周期程序和 react 方面, 其中一些图片是援用局部文章的
喜爱就给个赞吧, 该系列还在更新
react
组件生命周期图
生命周期 API
Col1 | Col2 |
---|---|
挂载 | |
constructor | 组件挂载之前被调用 |
shouldComponentUpdate(nextProps, nextState) | 在调用 render 办法之前调用,在初始化和后续更新都会被调用 |
render | 办法是 class 组件中惟一必须实现的办法,用于渲染 dom, render()办法必须返回 reactDOM |
componentDidMount | 组件挂载后 (插入 DOM 树后) 立刻调用 |
更新 | |
shouldComponentUpdate(nextProps, nextState) | 组件更新之前调用,能够管制组件是否进行更新,返回 true 时组件更新,返回 false 则不更新 |
getSnapshotBeforeUpdate(prevProps, prevState) | 最近一次的渲染输入被提交之前调用。也就是说,在 render 之后,行将对组件进行挂载时调用。 |
componentDidUpdate(prevProps, prevState, snapshot) | 更新后会被立刻调用。首次渲染不会执行, 第三个是“snapshot”参数传递 |
卸载 | |
componentWillUnmount | 组件行将被卸载或销毁时进行调用。 |
hooks 模仿生命周期
模仿 componentDidMount
useEffect(() => console.log('componentDidMount'), []);
模仿 shouldComponentUpdate
const MemoChild = React.memo(() => {...},
(prevProps, nextProps) => nextProps.count !== prevProps.count
)
模仿 componentDidUpdate
useEffect(() => console.log('mounted or updated'));
不仅能够拜访 componentDidUpdate,还能够拜访 componentDidMount,如果只想模仿 componentDidUpdate,
const ref = useRef(true);
useEffect(() => {if (ref.current) {ref.current = false;}
console.log('componentDidUpdate')
});
模仿 componentWillUnmount
useEffect(() => {return () => {console.log('模仿 componentWillUnmount');
}
}, []);
父子组件生命周期程序
Parent.tsx
为了更好的阐明, Parent.tsx 应用的是 hooks 写法
const MemoChild = React.memo(() => {useEffect(() => console.log("MemoChild. componentDidMount"), []);
console.log("MemoChild, function render");
return <div>memo</div>;
},
() => false);
function Parent() {let [show, setShow] = useState(true);
console.log("Parent, function render");
useEffect(() => console.log("Parent. componentDidMount"), []);
useEffect(() => () => console.log("Parent. componentWillUnmount"), []);
useEffect(() => console.log("Parent. show updated"), [show]);
return (
<ul>
<button
onClick={() => {setShow(!show);
}}
>
按钮
</button>
{show ? (<Child count2={2333}>
<MemoChild></MemoChild>
</Child>
) : (<MemoChild></MemoChild>)}
</ul>
);
}
Child.tsx
Child.tsx 应用的是 ReactComponent 写法
import React from "react";
interface countProp {count2: number;}
interface testState {count: number;}
class Child extends React.Component<countProp, testState> {static getDerivedStateFromProps(props: countProp, _state: testState) {console.log("Child. getDerivedStateFromProps");
return {count: props.count2,};
}
constructor(props: countProp) {super(props);
this.state = {count: 0,};
console.log("Child. constructor");
}
componentDidMount() {
this.setState({count: this.state.count + 123,});
console.log("Child. componentDidMount");
}
shouldComponentUpdate() {console.log("Child. shouldComponentUpdate");
return true;
}
getSnapshotBeforeUpdate() {console.log("Child. getSnapshotBeforeUpdate");
return null;
}
componentDidUpdate(props: any, state: any, snapshot: any) {console.log("Child. componentDidUpdate");
console.log("snapshot:", snapshot);
}
componentWillUnmount() {clearInterval(this.timerID);
console.log("Child. componentWillUnmount");
}
render() {const { children} = this.props;
const {count} = this.state;
console.log("Child. render");
return (
<>
<ul>
<li>state: {count}</li>
</ul>
{children}
</>
);
}
}
程序
下面渲染到挂载代码打印的程序
Parent, function render
Child. constructor
Child. getDerivedStateFromProps
Child. render
MemoChild, function render
Child. componentDidMount
MemoChild. componentDidMount
Parent. componentDidMount
Parent. show updated
在 Child.tsx 中 componentDidMount 周期中应用到 setData, 打印
Child. getDerivedStateFromProps
Child. shouldComponentUpdate
Child. render
Child. getSnapshotBeforeUpdate
Child. componentDidUpdate
snapshot: null
当咱们点击 parent 的按钮, 暗藏 Child.tsx, 打印
Parent, function render
MemoChild, function render
Child. componentWillUnmount
Parent. show updated
React 总结
组件到挂载期间, 先实现
Parent function > Child(constructor->getDerivedStateFromProps->render) > MemoChild function > componentDidMount (Child->MemoChild->Parent)
通过下面的程序能够发现, React 的周期是依照子组件程序挂载后, 才挂载 Parent
- ==render== 以及 ==render== 之前的生命周期,则 父组件先执行
- ==render== 以及 ==render== 之后的申明周期,则子组件先执行,并且是与父组件交替执行
Vue
生命周期图
生命周期 API
beforeCreate -> 应用 setup()
created -> 应用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
errorCaptured -> onErrorCaptured
renderTracked -> onRenderTracked
renderTriggered -> onRenderTriggered
activated -> onActivated
deactivated -> onDeactivated
onRenderTracked 和 onRenderTriggered
其中新增了两个 onRenderTracked 和 onRenderTriggered. 提供调试应用
- onRenderTracked(状态跟踪), 当组件第一次渲染时, 咱们设置响应值 (a) 的时候, 并且 get(比方 html 中的 {{a}})获取时触发
-
onRenderTriggered(状态触发), 扭转响应值触发
event 对象属性
- newValue 更新后变量的值
- oldValue 更新前变量的值
-
target 目前页面中的响应变量和函数
生命周期 API
Col1 | Col2 |
---|---|
setup | 在实例初始化之后、挂载之前, 无奈获取到 dom 节点。 |
beforeMount | 在挂载开始之前被调用:相干的 render 函数首次被调用 |
mounted | 在实例挂载实现后被调用,这时候传递给 app.mount 的元素曾经被新创建的 vm.$el 替换了 |
beforeUpdate | 在数据产生扭转后,DOM 被更新之前被调用。 |
updated | 在数据更改导致的虚构 DOM 从新渲染和更新结束之后被调用。 |
activated | 被 keep-alive 缓存的组件激活时调用。即页面显示在屏幕上时 |
deactivated | deactivated |
beforeUnmount | 在卸载组件实例之前调用。在这个阶段,实例依然是齐全失常的。 |
unmounted | 卸载组件实例后调用。 |
errorCaptured | 在捕捉一个来自后辈组件的谬误时被调用。 |
renderTracked | 跟踪虚构 DOM 从新渲染时调用。 |
renderTriggered | 当虚构 DOM 从新渲染被触发时调用。 |
父子周期程序
Parent.vue
<template>
<div>
<button @click="onBtnClick"> 按钮 </button>
<!-- <button @click="count++"> 按钮 count</button> -->
<Child v-if="bool"> </Child>
<div>{{count}}</div>
</div>
</template>
<script setup lang="ts">
import {onActivated, onDeactivated, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated, ref} from "vue";
import Child from "./Child.vue";
console.log("Parent setup");
const bool = ref(true);
let count = ref(0);
function onBtnClick() {bool.value = !bool.value;}
onMounted(() => {console.log("Parent onMounted");
count.value++;
});
onUpdated(() => {console.log("Parent onUpdated");
});
onRenderTracked((event) => {console.log("Parent onRenderTracked", event);
});
onRenderTriggered((event) => {console.log("Parent onRenderTriggered", event);
});
onActivated(() => {console.log("Parent onActivated");
});
onDeactivated(() => {console.log("Parent onDeactivated");
});
onUnmounted(() => {console.log("Parent onUnmounted");
});
</script>
Child.vue
<template>
<div>Child {{count}}</div>
</template>
<script lang="ts">
import {ref, defineComponent} from "vue";
export default defineComponent({setup() {const count = ref(0);
console.log("Child setup");
return {count};
},
mounted() {console.log("Child onMounted");
this.count++;
},
updated() {console.log("Child updated");
},
activated() {console.log("Child onActivated");
},
deactivated() {console.log("Child onDeactivated");
},
renderTracked(event) {console.log("Child onRenderTracked", event);
},
renderTriggered(event) {console.log("Child onRenderTriggered", event);
},
unmounted() {console.log("Child onUnmounted");
},
});
</script>
程序
Parent setup
Parent onRenderTracked {effect: ReactiveEffect, target: RefImpl, type: 'get', key: 'value'}
Parent onRenderTracked {effect: ReactiveEffect, target: RefImpl, type: 'get', key: 'value'}
Child setup
Child onRenderTracked {effect: ReactiveEffect, target: RefImpl, type: 'get', key: 'value'}
Child onMounted
Child onRenderTriggered {effect: ReactiveEffect, target: RefImpl, type: 'set', key: 'value', newValue: 1}
Parent onMounted
Parent onRenderTriggered {effect: ReactiveEffect, target: RefImpl, type: 'set', key: 'value', newValue: 1}
Child onActivated
Parent onActivated
Parent onUpdated
Child updated
vue 生命周期总结
Parent setup > Child setup > Child onMounted > Parent onMounted > Child onActivated > Parent onActivated
onActivated 更像 $nextTick, 所有子组件挂载后才进行, vue 与 react 类似, 都是挂载前父子程序, 挂载中 则是 子父程序
参考
深刻详解 React 生命周期
Vue3 官网文档