例子是在 jsrun.net 平台编写,不支持移动端平台,因此本文建议在 PC 端进行阅读。
所谓生命周期钩子函数(简称生命周期函数),指的是组件的建立、更新、销毁三个阶段所触发执行的函数。根据每一个阶段触发的钩子函数,咱们能够相应的作一些操做,如获取后端接口数据、监听事件、执行事件、执行定时器、移除事件、清理定时器等等。html
生命周期根据上面的三个阶段,本人总结为:vue
组件建立git
组件更新github
组件销毁面试
后续会根据这三大周期,分别说明生命周期函数。segmentfault
为了方便理解,本人在 http://jsrun.net 上编写了例子。(一个跟 jsfiddle 差很少的网站,国内 jsfiddle 被墙了)后端
首先看下官网的生命周期示意图,这里的生命周期函数都是针对浏览器端的,服务端目前只支持 beforeCreate
和 created
这两个生命周期函数。api
其中官网并无把 render
、renderError
函数概括为生命周期钩子函数。浏览器
其中 Has "el" option
对好比下:app
有 el
// 有el属性的状况下 new Vue({ el: "#app", beforeCreate: function() { console.log("调用了beforeCreate"); }, created: function() { console.log("调用了created"); }, beforeMount: function() { console.log("调用了beforeMount"); }, mounted: function() { console.log("调用了mounted"); } }); // 输出结果 // 调用了beforeCreate // 调用了created // 调用了beforeMount // 调用了mounted
无 el
// 有el属性的状况下 const vm new Vue({ beforeCreate: function() { console.log("调用了beforeCreate"); }, created: function() { console.log("调用了created"); }, beforeMount: function() { console.log("调用了beforeMount"); }, mounted: function() { console.log("调用了mounted"); } }); // 输出结果 // 调用了beforeCreate // 调用了created
无 el 时,若是须要挂载,能够这样处理:vm.$mount('#app')
。效果同样了,本质上没区别,只是用法更灵活。
实例化期会涉及到如下生命周期函数(执行顺序自上而下):
其中 beforeCreate
和 created
中间会触发 render
函数,若是有 template 会转换为 render 函数进行渲染。(固然若是组件的 定义了 render 函数,那么 render 函数优先级更高)
详细的例子请看 http://jsrun.net/LZyKp/edit
// 输出请看 右下角 Console 命令行工具 new Vue({ el: '#dynamic-component-demo', data: { num: 2, }, beforeCreate(){ console.log("beforeCreate",this.num,this.a); // 输出为 befoerCreate,, // this.num 数据还没监测,this.a 方法未绑定 }, created(){ console.log("created",this.num,this.a,this.$el); // 输出为 created, 2, function () { [native code] } }, beforeMount(){ console.log(this.$el.innerText); // 输出 {{ num }},仍是原来的 DOM 内容 }, mounted(){ console.log(this.$el.innerText); // 输出 2,已是 vue 渲染的 DOM 内容 }, methods: { a(){} } })
在实例初始化以后,数据观测 (data observer) 和 event/watcher 事件配置以前被调用。
在实例建立完成后被当即调用。在这一步,实例已完成如下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el
属性目前不可见。
在挂载开始以前被调用:相关的 render
函数首次被调用。$el
属性已经可见,但仍是原来的 DOM,并不是是新建立的。
el
被新建立的 vm.$el
替换,并挂载到实例上去以后调用该钩子。若是 root 实例挂载了一个文档内元素,当 mounted
被调用时 vm.$el
也在文档内。
注意 mounted
不会承诺全部的子组件也都一块儿被挂载。若是你但愿等到整个视图都渲染完毕,能够用 vm.$nextTick 替换掉 mounted
:
mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) }
存在期会涉及到如下生命周期函数:
Vue 须要改变数据才会触发组件从新渲染,才会触发上面的存在期钩子函数。其中 beforeUpdate
和 updated
中间会触发 render
函数。
例子请看 http://jsrun.net/8ZyKp/edit。
// 须要点击更新按钮 // 连续点击更新按钮,都会是 2 秒后不点击更新才会输出 “2 秒没更新了” // 输出请看 右下角 Console 命令行工具 new Vue({ el: '#dynamic-component-demo', data: { num: 2, }, beforeUpdate(){ clearTimeout(this.clearTimeout); this.clearTimeout = setTimeout(function(){ console.log("2 秒没更新了"); },2000); console.log("beforeUpdate",this.num,this.$el.innerText); // 第一次点击更新,输出为 beforeUpdate,3,点击更新 2 }, updated(){ console.log("updated",this.num,this.$el.innerText); // 第一次点击更新,输出为 updated,3,点击更新 3 }, methods: { updateComponent(){ this.num++; } } })
数据更新时,虚拟 DOM 变化以前调用,这里适合在更新以前访问现有的 DOM,好比手动移除已添加的事件监听器。
请不要在此函数中更改状态,不然会触发死循环。
数据更新和虚拟 DOM 变化以后调用。
当这个钩子被调用时,组件 DOM 已经更新,因此你如今能够执行依赖于 DOM 的操做。然而在大多数状况下,你应该避免在此期间更改状态。若是要相应状态改变,一般最好使用计算属性或 watcher 取而代之。
和 mounted 同样, updated
不会承诺全部的子组件也都一块儿被重绘。若是你但愿等到整个视图都重绘完毕,能够用 vm.$nextTick 替换掉 updated
:
updated: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been re-rendered }) }
请不要在此函数中更改状态,不然会触发死循环。
销毁期会涉及到如下生命周期函数:
例子请看 http://jsrun.net/QZyKp/edit。
// 切换 tab,看右下角 console 输出 Vue.component('tab-home', { template: '<div>Home component</div>', beforeDestroy(){ console.log("tab-home","beforeDestroy"); }, destroyed(){ console.log("tab-home","destroyed"); }, }) Vue.component('tab-posts', { template: '<div>Posts component</div>', beforeDestroy(){ console.log("tab-posts","beforeDestroy"); }, destroyed(){ console.log("tab-posts","destroyed"); }, }) Vue.component('tab-archive', { template: '<div>Archive component</div>', beforeDestroy(){ console.log("tab-archive","beforeDestroy"); }, destroyed(){ console.log("tab-archive","destroyed"); }, }) new Vue({ el: '#dynamic-component-demo', data: { currentTab: 'Home', tabs: ['Home', 'Posts', 'Archive'] }, computed: { currentTabComponent: function () { return 'tab-' + this.currentTab.toLowerCase() } } })
实例销毁以前调用,在这一步,实例仍然彻底可用。通常在这里移除事件监听器、定时器等,避免内存泄漏
Vue 实例销毁后调用。调用后,Vue 实例指示的全部东西都会解绑定,全部的事件监听器会被移除,全部的子实例也会被销毁。
因此若是须要用到 Vue 实例指示的所用绑定的东西,须要在 beforeDestroy 中使用。这么说,destroyed 函数能作的事,在 beforeDestroy 也能作,因此不必在 destroyed 函数中处理。
当组件激活的时候调用,能够参考构建组件 - keep-alive
当组件停用的时候调用,能够参考构建组件 - keep-alive
这个生命钩子详细请看官网,2.5.0 新增的,当捕获一个来自子孙组件的错误时被调用。
生命周期函数请不要使用 ES6 箭头函数,不然 this 指向会有问题。
请看这个例子 http://jsrun.net/cZyKp/edit。
// 输出请看 右下角 Console 命令行工具 new Vue({ el: '#dynamic-component-demo', data: { num: 2, }, created: ()=>{ console.log("created",this); // 输出为 created,[object Window] // this 指向不是 Vue 实例而是父级 this } })
既然每一个组件都有生命周期,那么父子、兄弟组件之间执行顺序有什么区别呢?
那么你首先须要了解到 template
何时转变成虚拟 DOM,而虚拟 DOM 又是在何时构建完成,而后又是何时挂载到真实的 DOM 上面。
这里不深刻说明 vue 原理,大概了解下 template 到虚拟 DOM 的过程:
template
-> AST 树
-> render 函数
实际上咱们关注点在 render 函数,能够了解下运行时-编译器-vs-只包含运行时。虚拟 DOM 就是在 render 函数中造成。若是你使用过 JSX,那么能够简单的把 JSX 理解为一个个的虚拟 DOM。固然 render 函数也会执行挂载到真实 DOM 的逻辑。
虚拟 DOM 就是一个树,vue 会把全部组件映射为虚拟 DOM 后渲染到真实 DOM 节点中,挂载不管如何必定是在最后执行的,因此全部组件 mounted 函数输出的日志必然是连在一块儿的,updated 也同理。
若是你不理解这句话,能够看这个例子 http://jsrun.net/7hyKp/edit。
render 函数才是父子组件生命周期函数的分割线。
而 render 函数的顺序以下。
实例化期:beforeMount
-> render
-> mounted
存在期:beforeUpdate
-> render
-> updated
详细请看在线例子,http://jsrun.net/vhyKp/edit。
详细请看在线例子,http://jsrun.net/NhyKp/edit。
学习和总结文章同步发布于 https://github.com/xianshanna...,有兴趣能够关注一下,一块儿学习和进步。