摘要:中的生命周期函數(shù)也可以稱之為生命周期鉤子函數(shù),在特定的時期,調(diào)用特定的函數(shù)。吊起鉤子函數(shù)調(diào)起鉤子函數(shù)問題為什么是一個數(shù)組卸載組件,會觸發(fā)一個這行代碼之后發(fā)生了什么背后實現(xiàn)原理。
簡介
關于Vue的生命周期函數(shù),目前網(wǎng)上有許多介紹文章,但也都只是分析了表象。這篇文檔,將結合Vue源碼分析,為什么會有這樣的表象。
Vue中的生命周期函數(shù)也可以稱之為生命周期鉤子(hook)函數(shù),在特定的時期,調(diào)用特定的函數(shù)。
隨著項目需求的不斷擴大,生命周期函數(shù)被廣泛使用在數(shù)據(jù)初始化、回收、改變Loading狀態(tài)、發(fā)起異步請求等各個方面。
而Vue實例的生命周期函數(shù)有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestpry、destroyed,8個。
本文假設讀者使用過Vue.js,但對相應的開發(fā)經(jīng)驗不做要求。如果你對Vue很感興趣,卻不知如何下手,建議你先閱讀官方文本
資源以下是這篇文章所需的資源,歡迎下載。
項目倉庫
Vue源碼筆記
表象我們在該頁面來研究,Vue的生命周期函數(shù)究竟會在何時調(diào)用,又會有什么不同的特性。強烈建議你直接將項目倉庫克隆至本地,并在真機環(huán)境中,跑一跑。Vue.js已經(jīng)添加在版本庫中,因此你不需要添加任何依賴,直接在瀏覽器中打開lifeCycle.html即可。
$ git clone https://github.com/AmberAAA/vue-guide編寫實現(xiàn)組件
定義template與data,使其在可以在屏幕實時渲染出來。
{ //... template: ``, data () { return { info: "" } } //... }
以beforeCreate為例,定義全部的證明周期函數(shù)。
beforeCreate() { console.group("------beforeCreate------"); console.log("beforeCreate called") console.log(this) console.log(this.$data) console.log(this.$el) this.info += "beforeCreate called屏幕輸出
" console.groupEnd(); }
在瀏覽器中打開lifeCycle.html,點擊掛載組件后,屏幕依次輸出created called 、beforeMount called 、mounted called 。表現(xiàn)出,在掛載組件后,info被created, beforeMount, mounted賦值,并渲染至屏幕上。但是本應在最開始就執(zhí)行的beforeCreate卻并沒有給info賦值。
卸載組件時,因為程序運行太快,為了方便觀察,特意為beforeDestroy和beforeDestroy函數(shù)在最后添加了斷點。發(fā)現(xiàn)點擊卸載組價后,Vue在v-if=true時會直接從文檔模型中卸載組件(此時組件已經(jīng)不在document)。
控制臺輸出的內(nèi)容非常具有代表性。
我們可以發(fā)現(xiàn),控制臺按照創(chuàng)建、掛載、更新、銷毀依次打印出對應的鉤子函數(shù)。展開來看
在觸發(fā)beforeCreate函數(shù)時,vue實例還尚未初始化$data,因此也就無法給$data賦值,也就很好的解釋了為什么在屏幕上,沒有渲染出beforeCreate called。同時,因為尚未被掛載,也就無法獲取到$el。
在觸發(fā)created函數(shù)時,其實就表明,該組件已經(jīng)被創(chuàng)建了。因此給info賦值后,待組件掛載后,視圖也會渲染出created called。
在觸發(fā)beforeMount函數(shù)時,其實就表明,該組件即將被掛載。此時組建表現(xiàn)出的特性與created保持一致。
在觸發(fā)mounted函數(shù)時,其實就表明,該組件已經(jīng)被掛載。因此給info賦值后,待組件掛載后,視圖也會渲染出mounted called,并且控制臺可以獲取到$el
觸發(fā)beforeUpdate與updated,分別表示視圖更新前后,更新前$data領先視圖,更新后,保持一致。在這兩個回調(diào)函數(shù)中,更改data時注意避免循環(huán)回調(diào)。
觸發(fā)beforeDestroy與destroyed,表示實例在銷毀前后,改變$data,會觸發(fā)一次updated,因在同一個函數(shù)中(下文會介紹)回調(diào),故捏合在一起說明。
名稱 | 觸發(fā)階段 | $data | $el |
---|---|---|---|
beforeCreate | 組件創(chuàng)建前 | ? | ? |
created | 組件創(chuàng)建后 | ? | ? |
beforeMount | 組件掛載前 | ? | ? |
mounted | 組件掛載后 | ? | ? |
beforeUpdate | 組件更新前 | ? | ? |
updated | 組件更新后 | ? | ? |
beforeDestroy | 組件創(chuàng)建前 | ? | ? |
destroyed | 組件創(chuàng)建前 | ? | ? |
Vue生命周期函數(shù)在源碼文件/src/core/instance/init.js中定義,并在/src/core/instance/init.js、src/core/instance/lifecycle.js、/src/core/observer/scheduler.js三個文件中調(diào)用了所有的生命周期函數(shù)
callHooK當在特定的使其,需要調(diào)用生命周期鉤子時,源碼只需調(diào)用callHook函數(shù),并傳入兩個參數(shù),第一個為vue實例,第二個為鉤子名稱。如下
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] if (handlers) { //? 這里為什么是數(shù)組?在什么情況下,數(shù)組的索引會大于1? for (let i = 0, j = handlers.length; i < j; i++) { try { handlers[i].call(vm) } catch (e) { handleError(e, vm, `${hook} hook`) } } } if (vm._hasHookEvent) { vm.$emit("hook:" + hook) } popTarget() }耍個流氓
callHook在打包時,并沒有暴露在全局作用域。但我們可以根據(jù)Vue實例來手動調(diào)用生命周期函數(shù)。試著在掛在組件后在控制臺輸入vue.$children[0].$options["beforeCreate"][0].call(vue.$children[0]),可以發(fā)現(xiàn)組件的beforeCreate鉤子已經(jīng)被觸發(fā)了。并且表示出了與本意相駁的特性。此時因為組件已經(jīng)初始化,并且已經(jīng)掛載,所以成功在控制臺打印出$el與$data,并在修改info后成功觸發(fā)了beforeUpdate與beforeUpdate
beforeCreate與createdVue會在/src/core/instance/init.js中通過initMixin函數(shù)對Vue實例進行進一步初始化操作。
export function initMixin (Vue: Class) { Vue.prototype._init = function (options?: Object) { /* .... */ vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, "beforeCreate") initInjections(vm) // resolve injections before data/props initState(vm) // 定義$data initProvide(vm) // resolve provide after data/props callHook(vm, "created") /* ... */ } }
可以看出在執(zhí)行callHook(vm, "beforeCreate")之前,Vue還尚未初始化data,這也就解釋了,為什么在控制臺beforeCreate獲取到的$data為undefined,而callHook(vm, "created")卻可以,以及屏幕上為什么沒有打印出beforeCreate called。
beforeMount與mountedVue在/src/core/instance/lifecycle.js中定義了mountComponent函數(shù),并在該函數(shù)內(nèi),調(diào)用了beforeMount與mounted。
export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { vm.$el = el // 組件掛載時 `el` 為`undefined` callHook(vm, "beforeMount") // 所以獲取到的`$el`為`undefined` /* ... */ // we set this to vm._watcher inside the watcher"s constructor // since the watcher"s initial patch may call $forceUpdate (e.g. inside child // component"s mounted hook), which relies on vm._watcher being already defined //! 挖個新坑 下節(jié)分享渲染watch。 經(jīng)過渲染后,即可獲取`$el` new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */) hydrating = false // manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true // 因為已經(jīng)渲染,`$el`此時已經(jīng)可以成功獲取 callHook(vm, "mounted") } return vm }beforeUpdate與updated
beforeUpdate與updated涉及到watcher,因此將會在以后的章節(jié)進行詳解。
beforeDestroy與destroyedVue將卸載組件的方法直接定義在原型鏈上,因此可以通過直接調(diào)用vm.$destroy()方法來卸載組件。
Vue.prototype.$destroy = function () { const vm: Component = this if (vm._isBeingDestroyed) { return } // 吊起`beforeDestroy`鉤子函數(shù) callHook(vm, "beforeDestroy") vm._isBeingDestroyed = true // remove self from parent const parent = vm.$parent if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { remove(parent.$children, vm) } // teardown watchers if (vm._watcher) { vm._watcher.teardown() } let i = vm._watchers.length while (i--) { vm._watchers[i].teardown() } // remove reference from data ob // frozen object may not have observer. if (vm._data.__ob__) { vm._data.__ob__.vmCount-- } // call the last hook... vm._isDestroyed = true // invoke destroy hooks on current rendered tree vm.__patch__(vm._vnode, null) // fire destroyed hook // 調(diào)起`destroyed`鉤子函數(shù) callHook(vm, "destroyed") // turn off all instance listeners. vm.$off() // remove __vue__ reference if (vm.$el) { vm.$el.__vue__ = null } // release circular reference (#6759) if (vm.$vnode) { vm.$vnode.parent = null } } }問題
vue.$children[0].$options["beforeCreate"]為什么是一個數(shù)組?
卸載組件,會觸發(fā)一個updated called?
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)這行代碼之后發(fā)生了什么?
beforeUpdate背后實現(xiàn)原理。
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98212.html
摘要:第一篇文章我會結合和的部分源碼,來說明注入生命周期的過程。說到源碼,其實沒有想象的那么難。但是源碼的調(diào)用樹會復雜很多。應用的業(yè)務代碼逐漸復雜,事件事件總線等通信的方式的弊端就會愈發(fā)明顯。狀態(tài)管理是組件解耦的重要手段。前言 這篇文章是【前端詞典】系列文章的第 13 篇文章,接下的 9 篇我會圍繞著 Vue 展開,希望這 9 篇文章可以使大家加深對 Vue 的了解。當然這些文章的前提是默認你對 ...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:其中的標志位什么時候設置呢,是在相應的鉤子觸發(fā)之后,具體看下面源碼怎么執(zhí)行鉤子呢沒錯,就是下面這個函數(shù)是自己傳入的等回調(diào)那是怎么用呢比如觸發(fā)就會這么調(diào)用很簡單不,直接拿到鉤子,然后遍歷執(zhí)行,綁定上下文對象。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 ...
閱讀 1275·2021-09-27 13:35
閱讀 2575·2021-09-06 15:12
閱讀 3392·2019-08-30 15:55
閱讀 2841·2019-08-30 15:43
閱讀 442·2019-08-29 16:42
閱讀 3454·2019-08-29 15:39
閱讀 3073·2019-08-29 12:28
閱讀 1251·2019-08-29 11:11