乐趣区

关于前端:vue3和react17三-生命周期ts

学习 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 官网文档

退出移动版