发布时间:2024-01-25 19:30
今天,我和大家一起来对vue生命周期做一个整理和思考,希望有缘人看到我的年度整理和思考,如果觉得靠谱呢,就交个朋友,如果觉得我整理的不足,就请指出,让我们一起进步,让我们2023年能共同思考。
☆ 文本将进行3个小模块,第一呢是老生常谈,然后再结合我的理解,说一说,vue的生命周期有哪些,每个阶段做了一些什么事情 ,而项目中,我们可以做一些什么事情;
☆ 第二呢,说一下嵌套父子组件情况下,生命周期的执行顺序;
☆ 第三,整理后思考一下,created和mounted两个钩子之间的时间差,可能会受哪些因素的影响
目录
三、created和mounted之间的时间差,影响因素有哪些?
生命周期这个命名还是非常贴切的,他就像我们每个人,一出生如何如何,上学了该干什么,中年又有那些责任和义务,转眼,你的事情还没有做完,可能整个人就完犊子了。
△ init阶段
而vue组件的生命周期呢,包含了从最初的new Vue()开始,表示他要出生了,这里初始化一些id,name的属性,合并一些option参数(后边的vm.$options.el会用到),我们熟知的一些函数挂载上,以利于后续使用。可以说new Vue() 就是一个小孩出生的过程,把该有的器官,肢体,基本能力都预备上,虽然现在还不能用,发育还不成熟。
let uid = 0
Vue.prototype._init = function(options) {
const vm = this
vm._uid = uid++ // uid累加唯一标识
vm.$options = mergeOptions( // 合并options
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
...
initLifecycle(vm) // 初始化
initEvents(vm) // v-on识别处理
initRender(vm) // render与vnode的结合
}
△ beforeCreate
正是有了前面的初始化,所以特意在这里为我们挂载了一个钩子函数,也就是这个生命周期。
其实你有没有发现,这个声明周期就像婴儿期的孩子,吃奶,哭,闹,啥也干不了。而我们在项目中基本也用不到。
callHook(vm, 'beforeCreate')
△ created
在beforeCreated到created这一阶段,数据依赖以形成,也就是我们常说的那个data函数,函数也可以调用,我们所写的template虚拟模板也逐渐被识别,但识别归识别,vnode还没转换成真正的DOM结构,所以data数据和模板中的v-model匹配不上。
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
注意这一步的vnode和template还只是内存中的活动,生成的只是内存中的一个模板字符串,后续需要真正的将虚拟模板挂载到页面中去。
△ befoteMounted
这一阶段承接created和mounted,data数据依赖有了,method方法有了,他需要不断的将内存中的虚拟模板字符串往DOM挂载,data数据是最新的,所以模板在根据虚拟模板的结构,根据data依赖的数据格式整合靠拢
△ mounted
这里基本就是孩子已经成熟了,数据依赖已有,methods已准备好,模板挂载为DOM,可以做DOM操作了。其实你发现,翻来覆去的说这么创新,那么实现,前端还是取DOM改DOM。这一点使得前端技术在很大一段时间,很长一段时间不会有多么惊人的不同。
△ beforeUpdate
现在动不动讲究数据驱动,当我们改变了data数据依赖,就开始beforeUpdate了,这个阶段更重要的是有一份真是DOM,有一份虚拟DOM,然后再根据新的data数据依赖,再生成一份虚拟DOM。紧接着就是我们时常有点懵的diff算法。经过一系列的胡乱操作之后,虚拟DOM更新完成,实现了data(Model数据)-----驱动----->view(视图)。
经常有人问:vue和react哪个更优秀?性能更高?其实这个结论没人下,一来一旦有权威的结论了,那么那么多人在使用着另一个技术栈,你让他怎么办?换技术栈?二来事实证明,不是人家底层技术栈做的好不好,而是你们团队一顿操作猛如虎,谁接手谁痛苦。
△ updated
这个阶段,最新的DOM已经更新出来,和data数据依赖的结构保持了一致。
这个时候蕴含着的一道题就是:vue出现了data已经更新,但视图没有更新的情况。
△ beforeDestroy
注意,这里是销毁前,用一个经典成语就是回光返照。虽然即将要销毁,但是data method 指令都还可用。但更应该注意的是真实DOM是否还存在的问题。
<template>
<div id="demo">007</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
num: 77
}
},
mounted() {
this.print7('method');
},
methods: {
print7(type) {
let demoDom = document.querySelector('#demo');
console.log(`--${type}--`, demoDom);
console.log(`--${type}--`, this.num);
}
},
beforeDestroy() {
this.print7('destroy');
}
}
</script>
△ destroyed
这个组件的数据,方法,模板,过滤器都销毁了。
小结:
Vue的生命周期包含初始化(init)、创建前(beforeCreate)、创建后(created)、载入前(beforeMount)、载入后(mounted)、更新前(beforeUpdate)、更新后(updated)、销毁前(beforeDestroy)、销毁后(destroyed) 这9大生命周期。
△ created
这个生命周期很明显数据data已有,真实DOM未挂载,所以我们可以做大图片的预加载,可以提前发送ajax请求,但这个时候容易出现远程的ajax请求比本地js代码执行还快,还没到mounted呢,response数据已经回来了。而返回的数据无法与DOM做绑定。所以可以用到nextTick
created() {
this.$nextTick(() => {
fetch({
url: 'http://xxx/user/list',
param
}).then((res) => {
})
})
},
有一些页面的关键初始化接口请求会从这里发出去,我们可以在请求接口前打印一下时间戳,在请求到数据后打印一下时间戳,进行数据采集上报,这样再通过用户访问的数据积累,就可以大体衡量出我们这个关键接口,在大量数据的访问下,平均的时长如何,是否更优优化的空间,甚至很多时候可以做为前端向后端甩锅的一个强硬性的数据性的理由。
△ mounted
这个阶段几乎只执行一次,但有一些监听浏览器行为的需要一直贯穿组件整个周期的事件可以放这里,例如监测浏览器滚动的事件:
mounted() {
window.onscroll(() => {
console.log('监听浏览器滚动');
})
},
△ updated
这个过程固然可以监听组件的数据变化,但往往我们又可以使用computed计算函数来更好的利用缓存特性来做一些数据的更新处理
△ destroyed
说实话,你的组件中如果没有制造一些全局变量,全局的定时器,这个生命周期可能一个月也用不到一次。
之前所做的一切努力,都烟消云散了,数据,方法,模板,过滤器。但一个人从这个世上离开了,并不是真的离开,直到最后一个还记着他的人离开了,才算真的离开了。
我们知道app.js从一开始挂载到DOM上,我们不断往这个单页面应用上挂载组件,其实整个项目应用简直就是一个庞然大物,被无数个小Vue组件悬挂着,所以某个组件如果在运行中的时候,产生了全局的变量,定时器等,是不会随着destroyed的销毁而消失的。
就像这个大树神经网络一样,某个枝干虽然不在了,但是神经还能触达某个其他区域。
首先是父组件,这里做了4步,布局组件模板、引入MyTitle组件、注册组件、传参
<template>
<my-title
:title="title" />
</template>
<script>
import MyTitle from './MyTitle';
export default {
name: 'HelloWorld',
components: { MyTitle },
data () {
return {
title: '这是一个标题组件',
}
},
created() {
console.log('父组件的created');
},
mounted() {
console.log('父组件的mounted');
},
}
</script>
然后是子组件的开发编写,主要是2步,通过props接收父组件参数,在子组件模板中渲染title参数
<template>
<div class="t">{{ title }}</div>
</template>
<script>
export default {
name: 'MyTitle',
props: ['title'],
data () {
return {}
},
created() {
console.log('子组件的created');
},
mounted() {
console.log('子组件的mounted');
}
}
</script>
显示结果:
这也正好印证的组件的生命周期顺序,首先是父组件的created周期,这个时候的重要事情是处理虚拟DOM的过程,所以凡是父组件中挂载的子组件,都需要先识别,转换,处理,最后挂载为真实的DOM,才能继续执行父组件的mounted周期。
如果这个问题,解说的再详细一点,就是其他不透露,已知created的钩子函数里只写了一行代码,打印了一个时间戳;mounted的钩子函数也只写了一行代码,打印了一个时间戳,那么他们的时间差值,影响因素会是哪些?
created() {
console.log((new Date()).getTime());
},
mounted() {
console.log((new Date()).getTime());
},
别的不说,这两个生命周期中间有个明显的beforeMounted周期卡着,如果在这个阶段做一件费事的事情,将是一个影响因素;
如果简单做一个demo,template模板里我们只写几个单纯的div元素,和真实项目中做了N个组件,组件中又嵌套了几个elementui组件比,再结合我们第二点所说的父子组件生命周期的执行顺序可知,template的复杂性也将是一个影响时间差值的因素;
mounted这个生命周期阶段呢,是真实DOM已挂载完成,之前所做的非常重要的一件事就是vnode与data数据层的结合,所以data数据如果结构复杂,map套了数组,数组里又套了map这些复合型数据结构,必将也是一个影响因素;
真实项目中,我们可能会将vue.min.js抽离以减少主业务js的体积大小,而主业务js也可能走了其他服务线路,很可能中间还会有其他的执行文件,所以,某些大型的平台,用户网络千差万别,可能执行时间上也会有所不同吧;
一旦引入了某个技术框架,很多人最爱比较的就是这个框架底层支持的代码体积是多少,为什么会有这个疑问呢,因为体积越多代码就越多啊,如果有人用着一部3年前的老人机,有人拿出了刚刚秒杀的旗舰机,执行速度肯定是不一样的。
我记得曾经问过一个人,他们的代码会在路边的某些公用设备上执行。你想一个不舍昼夜的机器,跑了3年了,和昨天新安装的机器比,是否会有所不同呢。
如果你觉得某一点对你有帮助了,请告诉我,让我继续发扬自己的优势;如果你觉得我哪里写的不好呢,请告诉我,我继续学习;我是csdn经海路大白狗,希望和你共同学习前端知识,探讨未来的程序人员。