生命周期
ES6中对象的简写,允许对象中只写属性名不写属性值,这时属性值=属性名所代表的变量
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子.
2.是什么: Vue在 关健时刻帮我们调用的些特殊名称的函数.
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象。
beforeCreate:
此时vue实例只有生命周期函数和一些默认的事件。
--------------------属性赋值、computed计算等
created:
此时已经有data等内容了,但还没有真实dom,对数据等进行修改不会触发update。在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
例如:数据的初始值就来自于后端,可以发送ajax,或者fetch请求获取数据,但是,此时不会触发updated函数
---------------解析模板的过程:render > template > el
beforeMount:
解析了但是还在内存中,this.$el也有值,但是没有渲染到页面中,即模板中的 {undefined{xx}}还没有内容。
--------------模板编译:用vm中的内容替换模板
mounted:
实例创建期间的最后一个生命周期函数。执行完了mounted函数表示整个Vue实例已经初始化完毕,脱离了创建阶段,进入到了运行阶段。在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted。所以mounted只执行一次。一般来说,我们在此处发送异步请求(ajax,fetch,axios等)、启动定时器、绑定自定义事件、订阅消息等[初始化操作]。
beforeUpdate:
在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程。数据更新了但是还没修改dom即没更新模板。
updated:
beforeDestroy:
清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。
destroyed:
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会再beforeDestroy操作数据。因为即便操作数据。也不会再触发更新流程了。
keep-alive
activated
deactivated
父子组件的生命周期
父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
双向数据绑定的原理
Vue 则采用的是数据劫持与发布订阅相结合的方式实现双向绑定,数据劫持主要通过 Object.defineProperty()
来实现,它里面的getter、setter属性。
在编译 HTML 的过程中,会为每个与数据绑定相关的节点生成一个订阅者 watcher,watcher 会将自己添加到相应属性的 dep 容器中。
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。(数据劫持)在setter函数里面,如果数据变化,就会去通知所有订阅者,订阅者们就会去执行对应的更新的函数。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。是Observer和Compile之间通信的桥梁,主要做的事情是:
(1)在自身实例化时往属性订阅器(dep)里面添加自己
(2)自身必须有一个update()方法
(3)待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。(直接固定某个节点进行替换数据的,所以接下来需要实现一个解析器Compile来做解析和绑定工作。)
render
createElement有三个参数,第一个参数是创建的标签用字符串,可以是引入的组件,第二个参数是一个对象,属性是attrs(给标签添加属性值)、on(给标签添加事件),或者是传给组件等;第三个参数的标签里面的内容,如文本,它用vue文件中的变量时,用模板字符串。
render: h => h(App)
是下面内容的缩写:
render: function (createElement) {
return createElement(App);
}
template和虚拟dom
- 将模板解析为AST(抽象语法树)—— 解析器parse
- 遍历AST,对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化 —— 优化器optimize
- 使用AST生成render函数代码字符串—— 代码生成器
AST
第一步,词法分析,也叫做扫描scanner。它读取我们的代码,然后把它们按照预定的规则合并成一个个的标识tokens。同时,它会移除空白符,注释,等。最后,整个代码将被分割进一个tokens列表(或者说一维数组)。当词法分析源代码的时候,它会一个一个字母地读取代码,所以很形象地称之为扫描-scans;当它遇到空格,操作符,或者特殊符号的时候,它会认为一个话已经完成了。
第二步,语法分析,也解析器。它会将词法分析出来的数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。
创建虚拟结点
-
在首次渲染时,将
虚拟DOM
转为真实的DOM
,vm._update
会调用__patch__
方法,而patch
方法实际上则是封装了createPatchFunction
方法。 -
在
createPatchFunction
方法中,先获取旧节点的父元素,然后将虚拟DOM转为真实DOM,插入到旧节点的父元素下。 -
在将虚拟DOM转为真实DOM时,又将虚拟DOM分为组件类型的虚拟DOM、和普通的虚拟DOM。不同类型的DOM,处理方式也不一样。组件类型的
-
而普通的虚拟DOM,处理方式如上图。
patch
比较:oldV是否是虚拟节点,是:判断新旧是不是同一结点;不是:把oldV封装成虚拟
是同一节点:判断是否在同一片内存;否:删除旧增加新
是同一个内存对象:不操作;否:判断newV有无text值
new有text值:更新oldV的innerText值;无:说明newV有children,判断old
oldV有text值:删旧的text,更新DOM用newV的children
oldV有children(即两个都有):进行diff算法
diff 比较只会在同层级进行, 不会跨层级比较。
- 对newV和oldV都有两个指针指向第一个元素和最后一个元素,
- 从前往后扫,命中就后移否则从后往前直到后指针索引<前指针索引
- 前不匹配,从后开始,若不匹配,新后与旧前命中则先patch处理,再把旧节点移动都旧后后面
- 新前与旧后,命中,放到旧前处
路由守卫
全局守卫
vue-router全局有三个守卫:
- router.beforeEach 全局前置守卫 进入路由之前
- router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
- router.afterEach 全局后置钩子 进入路由之后
使用方法:
// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => {
next();
});
router.beforeResolve((to, from, next) => {
next();
});
router.afterEach((to, from) => {
console.log('afterEach 全局后置钩子');
});
to,from,next 这三个参数:
to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。
next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。
-
next() 进入该路由。
-
next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。
-
next 跳转新路由,当前的导航被中断,重新开始一个新的导航。
我们可以这样跳转:next('path地址')或者next({path:''})或者next({name:''}) 且允许设置诸如 replace: true、name: 'home' 之类的选项 以及你用在router-link或router.push的对象选项。
路由独享守卫
如果你不想全局配置守卫的话,你可以为某些路由单独配置守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖
// ...
}
}
]
})
路由组件内的守卫:
- beforeRouteEnter 进入路由前
- beforeRouteUpdate (2.2) 路由复用同一个组件时
- beforeRouteLeave 离开当前路由时
文档中的介绍:
beforeRouteEnter (to, from, next) {
// 在路由独享守卫后调用 不!能!获取组件实例 `this`,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用 可以访问组件实例 `this`
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用,可以访问组件实例 `this`
}
复制代码
beforeRouteEnter访问this
因为钩子在组件实例还没被创建的时候调用,所以不能获取组件实例 this
,可以通过传一个回调给next
来访问组件实例 。
但是回调的执行时机在mounted后面,所以在我看来这里对this的访问意义不太大,可以放在created
或者mounted
里面。
beforeRouteEnter (to, from, next) {
console.log('在路由独享守卫后调用');
next(vm => {
// 通过 `vm` 访问组件实例`this` 执行回调的时机在mounted后面,
})
}
beforeRouteLeave:
导航离开该组件的对应路由时调用,我们用它来禁止用户离开,比如还未保存草稿,或者在用户离开前,将setInterval
销毁,防止离开之后,定时器还在调用。
beforeRouteLeave (to, from , next) {
if (文章保存) {
next(); // 允许离开或者可以跳到别的路由 上面讲过了
} else {
next(false); // 取消离开
}
}
完整的路由导航解析流程(不包括其他生命周期):
- 触发进入其他路由。
- 调用要离开路由的组件守卫
beforeRouteLeave
- 调用局前置守卫:
beforeEach
- 在重用的组件里调用
beforeRouteUpdate
- 调用路由独享守卫
beforeEnter
。 - 解析异步路由组件。
- 在将要进入的路由组件中调用
beforeRouteEnter
- 调用全局解析守卫
beforeResolve
- 导航被确认。
- 调用全局后置钩子的
afterEach
钩子。 - 触发DOM更新(
mounted
)。 - 执行
beforeRouteEnter
守卫中传给 next 的回调函数
新增了三个组件:Fragment 支持多个根节点、Suspense 可以在组件渲染之前的等待时间显示指定内容、Teleport 可以让子组件能够在视觉上跳出父组件(如父组件overflow:hidden)
Vue3对比Vue2
- 新增指令 v-memo,可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间
支持 Tree-Shaking,会在打包时去除一些无用代码,没有用到的模块,使得代码打包体积更小 - 新增 Composition API 可以更好的逻辑复用和代码组织,同一功能的代码不至于像以前一样太分散,虽然 Vue2 中可以用 minxin 来实现复用代码,但也存在问题,比如方法或属性名会冲突,代码来源也不清楚等
- 用 Proxy 代替 Object.defineProperty 重构了响应式系统,可以监听到数组下标变化,及对象新增属性,因为监听的不是对象属性,而是对象本身,还可拦截 apply、has 等13种方法
- 重构了虚拟 DOM,在编译时会将事件缓存、将 slot 编译为 lazy 函数、保存静态节点直接复用(静态提升)、以及添加静态标记、Diff 算法使用 最长递增子序列 优化了对比流程,使得虚拟 DOM 生成速度提升 200%
- 支持在 里使用 v-bind,给 CSS 绑定 JS 变量(color: v-bind(str))
- 用 setup 代替了 beforeCreate 和 created 这两个生命周期
- 新增了开发环境的两个钩子函数,在组件更新时 * onRenderTracked 会跟踪组件里所有变量和方法的变化、每次触发渲染时 onRenderTriggered 会返回发生变化的新旧值,可以让我们进行有针对性调试
- 毕竟 Vue3 是用 TS 写的,所以对 TS 的支持度更好
Vue3 不兼容 IE11