生命周期
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操作数据。因为即便操作数据。也不会再触发更新流程了。
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来做解析和绑定工作。)
createElement有三个参数,第一个参数是创建的标签用字符串,可以是引入的组件,第二个参数是一个对象,属性是attrs(给标签添加属性值)、on(给标签添加事件),或者是传给组件等;第三个参数的标签里面的内容,如文本,它用vue文件中的变量时,用模板字符串。
render: h => h(App)
是下面内容的缩写:
render: function (createElement) {
return createElement(App);
}
第一步,词法分析,也叫做扫描scanner。它读取我们的代码,然后把它们按照预定的规则合并成一个个的标识tokens。同时,它会移除空白符,注释,等。最后,整个代码将被分割进一个tokens列表(或者说一维数组)。当词法分析源代码的时候,它会一个一个字母地读取代码,所以很形象地称之为扫描-scans;当它遇到空格,操作符,或者特殊符号的时候,它会认为一个话已经完成了。
第二步,语法分析,也解析器。它会将词法分析出来的数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。
创建虚拟结点
在首次渲染时,将虚拟DOM
转为真实的DOM
,vm._update
会调用 __patch__
方法,而 patch
方法实际上则是封装了 createPatchFunction
方法。
在 createPatchFunction
方法中,先获取旧节点的父元素,然后将虚拟DOM转为真实DOM,插入到旧节点的父元素下。
在将虚拟DOM转为真实DOM时,又将虚拟DOM分为组件类型的虚拟DOM、和普通的虚拟DOM。不同类型的DOM,处理方式也不一样。组件类型的
而普通的虚拟DOM,处理方式如上图。
比较:oldV是否是虚拟节点,是:判断新旧是不是同一结点;不是:把oldV封装成虚拟
是同一节点:判断是否在同一片内存;否:删除旧增加新
是同一个内存对象:不操作;否:判断newV有无text值
new有text值:更新oldV的innerText值;无:说明newV有children,判断old
oldV有text值:删旧的text,更新DOM用newV的children
oldV有children(即两个都有):进行diff算法
vue-router全局有三个守卫:
使用方法:
// 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是将要进入和将要离开的路由对象,路由对象指的是平时通过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 (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
钩子。mounted
)。beforeRouteEnter
守卫中传给 next 的回调函数