锉其兑,解其纷,和其光,同其尘 --- 《道德经》第四章 和光同尘
- React 生命周期 16/15
- Vue 2/3
- Angular
- Svelte
浏览加载
理解 web 页面的生命周期,就是在合适的时候做合适的事情,让事情合理, 和光同尘。
- Script 脚本
- DOMContentLoaded (此时其实 dom 结果已经完成,可以操作 dom, 资源文件可能还没有加载文件,异步的 js 还没有执行完毕)
- image onload
- iframe onload
- readyState: complete
- onload 所有资源加载完毕
- beforeunload 事件在用户准备离开页面,在 unload 事件之前触发
- unload 用户最终离开时会触发该事件
一般来说,大多数的操作我们都应该放在 DOMContentLoaded 事件中执行,而不要放在 window.onload 中执行。
文档加载状态
document.readyState 可以返回文档的加载状态,文档的加载状态有一下四种:
- uninitialized - 还未开始载入
- loading - 载入中
- interactive - 已加载,文档与用户可以开始交互
- complete - 载入完成
它对应的有一个事件 readystatechange, 我们可以通过这个事件来监听文件的变化。
document.addEventListener("readystatechange", () => {
console.log(document.readyState);
});
document.addEventListener("onload", () => {
console.log("资源加载完毕");
});
document.addEventListener("beforunload", () => {
console.log("将要卸载");
});
document.addEventListener("unload", () => {
console.log("卸载");
});
DOMContentLoaded
顾名思义,当 HTML 文档加载完毕和解析完毕之后,DOMContentLoaded 就会被触发:
document.addEventListener("DOMContentLoaded", () => {
console.log("HTML 文档加载完毕和解析完毕");
});
需要注意的是: 无需等待样式表、图像和子框架的完成加载。
问题
从输入 url 到页面加载完毕发生了哪些事情?
React 生命周期和钩子函数
类组件生命周期
-
挂载阶段
- constructor(props)
- static getDerivedStateFromProps(props, state):存在只有一个目的:让组件在 props 变化时更新 state
- render()
- componentDidMount()
- UNSAFE_componentWillMount() 因为使用 fiber 结构,UNSAFE_componentWillMount 已经不适合在新的组件中使用了
-
更新阶段
- 触发组件更新的三种方式:newProps, newState, forceUpdate
- static getDerivedStateFromProps()
- shouldComponentUpdate(nextProps, nextState)
- render()
- getSnapshotBeforeUpdate(prevProps, prevState)
- 通过 Ref 获取 dom
- componentDidUpdate(prevProps, prevState, snapshot)
- 不安全的生命周期
- UNSAFE_componentWillUpdate()
- UNSAFE_componentWillReceiveProps()
-
卸载阶段
- componentWillUnmount()
-
处理错误
- static getDerivedStateFromError(error)
- componentDidCatch(error, info)
每一个阶段在 React 16 中又分为 3 个期间
- 渲染期
- 预提交期
- 提交期
注意
静态方法: getDerivedStateFromProps, 其实很简单,就是要在组件渲染之前,可以根据外部数据 props 修改数据 state。
- 必须要有返回值,返回决定了 state 的状态
- 可以返回 null 表示没有任何修改
- 不能访问 this 此时 this 是 undefined ,这能是他能修改 state 的真实原因,不能调用 setState 方法.
shouldComponentUpdate 方法
可以可以决定组件是否真正的更新,如果 shouldComponentUpdate 返回值为 true, 则重新执行 render 函数,更新。返回 false 则反之。
getSnapshotBeforeUpdate(prevProps, prevState)
需要返回值 Snapshot 或者 null,任何返回值将作为参数传递给 componentDidUpdate()。
componentDidUpdate(prevProps, prevState, snapshot)
- 一个简单的示例
import React from "react";
// components
import Layout from "../components/layout";
class Lifecycle extends React.Component {
constructor(props) {
console.log("constructor`s:", props);
// super(props)
this.state = { a: 1 };
this.handleClick = this.handleClick.bind(this);
}
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps:props", props);
console.log("getDerivedStateFromProps:state", state);
// 可以返回一个新的 state 对象, getDerivedStateFromProp 在 render 函数之前执行,
// 在进行方法中不能通过 this 访问 setState, 此时的 this 为 undefined
return null;
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate nextState", nextProps);
console.log("shouldComponentUpdate nextState", nextState);
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate", prevState);
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate", prevProps);
console.log("componentDidUpdate", prevState);
console.log("componentDidUpdate", snapshot);
}
handleClick() {
this.setState({
a: this.state.a + 1
});
}
render() {
return (
<Layout>
<div>{this.state.a}</div>
<div>{this.state.b}</div>
<button onClick={this.handleClick}>+1</button>
<div>132</div>
</Layout>
);
}
}
export default Lifecycle;
hooks
hooks 主要是解决函数组件,不能使用 state 的能力,同时函数式编程有强大的组合能力,使得代码更加容易维护
- state
- props
- 钩子函数取代生命周期
- 一个简单的示例:
import React from "react";
// components
import Layout from "../components/layout";
const Transition = () => {
const [style, setStyle] = React.useState({
background: "blue"
});
const onHandleTransition = () => {
if (style.background === "blue") {
setStyle({
background: "red",
transition: "all 1s linear .2s",
color: "white"
});
} else {
setStyle({
background: "blue",
transition: "all 1s linear .2s",
color: "orange"
});
}
};
return (
<Layout>
<div style={style}>123</div>
<button onClick={onHandleTransition}>点击触发过渡</button>
</Layout>
);
};
export default Transition;
特点
- 没有 this
- 使用函数式编程
- 没有生命周期,部分功能交给了函数处理副作用 useEffect
Vue 生命周期
Vue2 生命周期
Vue2 生命周期与 Vue 实例紧密结合:
- 创建期
- beforeCreate
- created (开始能访问 this, 也就是 Vue 实例,因为此时 Vue 的初始化 init 工作才完成)
- 挂载
- beforeMount
- mounted (开始能访问 dom, 此时 dom 挂载完毕)
- 更新期
- beforeUpdate
- updated
- 卸载期
- beforeDestroy
- destroyed
我们看到 Vue2 的生命周期是非常清晰的。有两个重要的时间点:
- created, 是 Vue 初始化完毕,能够开始访问 Vue 组件实例
- mounted dom 挂载完毕,能处理 dom 以及其他的副作用
Vue3 生命周期钩子
Vue3 在生命周期钩子上发生了变化,vue3的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用:
beforeCreate -> 使用setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured
- 简单示例
import { onMounted, onUpdated } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('on mounted')
})
onUpdated(() => {
console.log('on updated')
})
}
}
小结
Vue2 与 Vue3 的生命周期在用法上有很大的变化,需要配合 setup 函数,进行使用。
keep-alive 组件组件独享生命周期钩子
本质:keep-alive 是用来缓存 vm 实例的,具有三个 props 和两个周期钩子函数
-
props:include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
-
props:exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
-
props:max - 数字。最多可以缓存多少组件实例。
-
activated 被 keep-alive 缓存的组件激活时调用。
-
deactivated 被 keep-alive 缓存的组件停用时调用。
transiton 组件 也有钩子函数
- before-enter
- before-leave
- before-appear
- enter
- leave
- appear
- after-enter
- after-leave
- after-appear
- enter-cancelled
- leave-cancelled (v-show only)
- appear-cancelled
当我们需要灵活的控制 Vue 的过渡组件的效果的时候,可以使用 transition 组件提供的钩子函数
Vue-Router 生命周期钩子或者守卫
Vue-Router 也提供可很多生命周期钩子函数:
钩子与守卫的区别: 钩子函数不接受 next 方法,没有守卫的能力。
- 全局钩子函数
- beforeEach 全局前置守卫
- beforeResolve 全局解析守卫
- afterEach 全局后置钩子 钩子不会接受 next
- 路由配置独享守卫
- beforeEnter
- 组件内部的路由守卫钩子
- beforeRouteEnter
- beforeRouteUpdate (2.2 新增)
- beforeRouteLeave
也就是说只有一个 afterEach 后置钩子:其他的六个守卫,有 next 方法。
一个路由切换,钩子函数的调用:
- 触发当前组件离开行为或者事件
- 当前组件钩子函数 beforeRouteLeave 被触发,表示在路由离开之前,调用的生命周期钩子函数
- 全局前置钩子响应,知道要进行路由变化了,beforeEach 进行调用。
- 将要进入的组件的路由守卫 beforeRouteUpdate 将要调用 从将离开的组件,进入将要激活的组件之间的一次互动
- 在将要激活的组件的钩子函数执行之后,机会进入路由独享守卫,看看路由有没有要干的事情
- 解析异步组件
- 在激活的组件中调用 beforeRouteEnter,说明从这个时候一个开始进入新的组了
- 此时全局的解析钩子开始工作了,调用 beforeResolve
- 解析完毕之后导航被确定下来
- 定下来之后调用全局后置钩子 afterEach
- 触发 DOM 更新
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入
关于路由有一个重要的点: 记住参数或查询的改变并不会触发进入/离开的导航守卫.你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。还可以绑定一个特别的 key>
Angular
Angular 指令的生命周期,它是用来记录指令从创建、应用及销毁的过程。
生命周期
- ngOnChanges() 初始化输入属性 ,Angular(重新)设置数据绑定输入属性时的响应。
- ngOnInit() 初始化其他属性,在 Angular 首次显示数据绑定属性并设置指令/组件的输入属性后初始化指令/组件。在第一次之后 调用一次 ngOnChanges() 。
- ngDoCheck() 组件变更检测,检测 Angular 无法或不会自行检测的更改并采取相应措施。在每次更改检测运行期间,在 ngOnChanges() 和之后立即调用 ngOnInit()。
- ngAfterContentInit() 投影内容初始化,在 Angular 将外部内容投影到组件的视图/指令所在的视图后进行响应。在第一次之后 调用一次
- ngAfterContentChecked() 针对投影内容的变更检测在 Angular 检查投射到指令/组件中的内容后响应。在 ngAfterContentInit() 随后和随后的每一次调用之后 ngDoCheck()。
- ngAfterViewInit() 初始化完组件视图及其子视图之后调用 ,在 Angular 初始化组件的视图和子视图/指令所在的视图后响应。在第一次之后 调用一次 ngAfterContentChecked()。
一般进行 DOM 操作 - ngAfterViewChecked() 每次做完组件视图和子视图的变更检测之后调用,在 Angular 检查组件的视图和子视图/指令所在的视图后响应。在 ngAfterViewInit() 随后和随后的每一次调用之后 ngAfterContentChecked()。
- ngOnDestroy() 当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏,就在 Angular 破坏指令/组件之前进行清理。取消订阅 Observable 并分离事件处理程序以避免内存泄漏。在 Angular 破坏指令/组件之前 调用。组件销毁时执行
1. 指令与组件共有的钩子
ngOnChanges ngOnInit ngDoCheck ngOnDestroy
2. 组件特有的钩子
ngAfterContentInit ngAfterContentChecked ngAfterViewInit ngAfterViewChecked
3. 生命周期调用顺序
ngOnChanges - 当数据绑定输入属性的值发生变化时调用 ngOnInit - 在第一次 ngOnChanges 后调用 ngDoCheck - 自定义的方法,用于检测和处理值的改变 ngAfterContentInit - 在组件内容初始化之后调用 ngAfterContentChecked - 组件每次检查内容时调用 ngAfterViewInit - 组件相应的视图初始化之后调用 ngAfterViewChecked - 组件每次检查视图时调用 ngOnDestroy - 指令销毁前调用
Svelte
Svelte 生命周期:
- onMount 在 挂载到 dom 之后执行
- beforeUpdate 在任何状态更改后立即在组件更新之前运行的回调。
- afterUpdate 在组件更新后立即运行的回调。
- onDestroy 在组件更新后立即运行的回调。 唯一一个服务器端组件内部运行
- tick 在应用任何暂挂状态更改后解决,或者在下一个微任务中(如果没有更改)解决
由于 Svelte 没有生命周期,不要初始化一些与虚拟 dom 相关的内容,所以生命周期也特简单。
<script>
import { onMount,beforeUpdate, afterUpdate, onDestroy } from 'svelte';
onMount(() => {
console.log('the component has mounted');
});
// 有返回值,则用于卸载是调用,类似于 React 的钩子函数 useEffect
onMount(() => {
const interval = setInterval(() => {
console.log('beep');
}, 1000);
return () => clearInterval(interval);
});
beforeUpdate(() => {
console.log('the component is about to update');
});
afterUpdate(() => {
console.log('the component just updated');
});
onDestroy(() => {
console.log('the component is being destroyed');
});
beforeUpdate(async () => {
console.log('the component is about to update');
await tick();
console.log('the component just updated');
});
</script>
todo
- url 从输入经历什么?
- 微信小程序页面生命周期,组件生命周期
- 更好的示例
- 其他