摘要:中的生命周期函數也可以稱之為生命周期鉤子函數,在特定的時期,調用特定的函數。吊起鉤子函數調起鉤子函數問題為什么是一個數組卸載組件,會觸發一個這行代碼之后發生了什么背后實現原理。
簡介
關于Vue的生命周期函數,目前網上有許多介紹文章,但也都只是分析了表象。這篇文檔,將結合Vue源碼分析,為什么會有這樣的表象。
Vue中的生命周期函數也可以稱之為生命周期鉤子(hook)函數,在特定的時期,調用特定的函數。
隨著項目需求的不斷擴大,生命周期函數被廣泛使用在數據初始化、回收、改變Loading狀態、發起異步請求等各個方面。
而Vue實例的生命周期函數有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestpry、destroyed,8個。
本文假設讀者使用過Vue.js,但對相應的開發經驗不做要求。如果你對Vue很感興趣,卻不知如何下手,建議你先閱讀官方文本
資源以下是這篇文章所需的資源,歡迎下載。
項目倉庫
Vue源碼筆記
表象我們在該頁面來研究,Vue的生命周期函數究竟會在何時調用,又會有什么不同的特性。強烈建議你直接將項目倉庫克隆至本地,并在真機環境中,跑一跑。Vue.js已經添加在版本庫中,因此你不需要添加任何依賴,直接在瀏覽器中打開lifeCycle.html即可。
$ git clone https://github.com/AmberAAA/vue-guide編寫實現組件
定義template與data,使其在可以在屏幕實時渲染出來。
{ //... template: ``, data () { return { info: "" } } //... }
以beforeCreate為例,定義全部的證明周期函數。
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 。表現出,在掛載組件后,info被created, beforeMount, mounted賦值,并渲染至屏幕上。但是本應在最開始就執行的beforeCreate卻并沒有給info賦值。
卸載組件時,因為程序運行太快,為了方便觀察,特意為beforeDestroy和beforeDestroy函數在最后添加了斷點。發現點擊卸載組價后,Vue在v-if=true時會直接從文檔模型中卸載組件(此時組件已經不在document)。
控制臺輸出的內容非常具有代表性。
我們可以發現,控制臺按照創建、掛載、更新、銷毀依次打印出對應的鉤子函數。展開來看
在觸發beforeCreate函數時,vue實例還尚未初始化$data,因此也就無法給$data賦值,也就很好的解釋了為什么在屏幕上,沒有渲染出beforeCreate called。同時,因為尚未被掛載,也就無法獲取到$el。
在觸發created函數時,其實就表明,該組件已經被創建了。因此給info賦值后,待組件掛載后,視圖也會渲染出created called。
在觸發beforeMount函數時,其實就表明,該組件即將被掛載。此時組建表現出的特性與created保持一致。
在觸發mounted函數時,其實就表明,該組件已經被掛載。因此給info賦值后,待組件掛載后,視圖也會渲染出mounted called,并且控制臺可以獲取到$el
觸發beforeUpdate與updated,分別表示視圖更新前后,更新前$data領先視圖,更新后,保持一致。在這兩個回調函數中,更改data時注意避免循環回調。
觸發beforeDestroy與destroyed,表示實例在銷毀前后,改變$data,會觸發一次updated,因在同一個函數中(下文會介紹)回調,故捏合在一起說明。
名稱 | 觸發階段 | $data | $el |
---|---|---|---|
beforeCreate | 組件創建前 | ? | ? |
created | 組件創建后 | ? | ? |
beforeMount | 組件掛載前 | ? | ? |
mounted | 組件掛載后 | ? | ? |
beforeUpdate | 組件更新前 | ? | ? |
updated | 組件更新后 | ? | ? |
beforeDestroy | 組件創建前 | ? | ? |
destroyed | 組件創建前 | ? | ? |
Vue生命周期函數在源碼文件/src/core/instance/init.js中定義,并在/src/core/instance/init.js、src/core/instance/lifecycle.js、/src/core/observer/scheduler.js三個文件中調用了所有的生命周期函數
callHooK當在特定的使其,需要調用生命周期鉤子時,源碼只需調用callHook函數,并傳入兩個參數,第一個為vue實例,第二個為鉤子名稱。如下
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] if (handlers) { //? 這里為什么是數組?在什么情況下,數組的索引會大于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在打包時,并沒有暴露在全局作用域。但我們可以根據Vue實例來手動調用生命周期函數。試著在掛在組件后在控制臺輸入vue.$children[0].$options["beforeCreate"][0].call(vue.$children[0]),可以發現組件的beforeCreate鉤子已經被觸發了。并且表示出了與本意相駁的特性。此時因為組件已經初始化,并且已經掛載,所以成功在控制臺打印出$el與$data,并在修改info后成功觸發了beforeUpdate與beforeUpdate
beforeCreate與createdVue會在/src/core/instance/init.js中通過initMixin函數對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") /* ... */ } }
可以看出在執行callHook(vm, "beforeCreate")之前,Vue還尚未初始化data,這也就解釋了,為什么在控制臺beforeCreate獲取到的$data為undefined,而callHook(vm, "created")卻可以,以及屏幕上為什么沒有打印出beforeCreate called。
beforeMount與mountedVue在/src/core/instance/lifecycle.js中定義了mountComponent函數,并在該函數內,調用了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 //! 挖個新坑 下節分享渲染watch。 經過渲染后,即可獲取`$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 // 因為已經渲染,`$el`此時已經可以成功獲取 callHook(vm, "mounted") } return vm }beforeUpdate與updated
beforeUpdate與updated涉及到watcher,因此將會在以后的章節進行詳解。
beforeDestroy與destroyedVue將卸載組件的方法直接定義在原型鏈上,因此可以通過直接調用vm.$destroy()方法來卸載組件。
Vue.prototype.$destroy = function () { const vm: Component = this if (vm._isBeingDestroyed) { return } // 吊起`beforeDestroy`鉤子函數 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 // 調起`destroyed`鉤子函數 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"]為什么是一個數組?
卸載組件,會觸發一個updated called?
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)這行代碼之后發生了什么?
beforeUpdate背后實現原理。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98212.html
摘要:第一篇文章我會結合和的部分源碼,來說明注入生命周期的過程。說到源碼,其實沒有想象的那么難。但是源碼的調用樹會復雜很多。應用的業務代碼逐漸復雜,事件事件總線等通信的方式的弊端就會愈發明顯。狀態管理是組件解耦的重要手段。前言 這篇文章是【前端詞典】系列文章的第 13 篇文章,接下的 9 篇我會圍繞著 Vue 展開,希望這 9 篇文章可以使大家加深對 Vue 的了解。當然這些文章的前提是默認你對 ...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:其中的標志位什么時候設置呢,是在相應的鉤子觸發之后,具體看下面源碼怎么執行鉤子呢沒錯,就是下面這個函數是自己傳入的等回調那是怎么用呢比如觸發就會這么調用很簡單不,直接拿到鉤子,然后遍歷執行,綁定上下文對象。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 ...
閱讀 1265·2021-09-27 13:35
閱讀 2563·2021-09-06 15:12
閱讀 3380·2019-08-30 15:55
閱讀 2829·2019-08-30 15:43
閱讀 432·2019-08-29 16:42
閱讀 3446·2019-08-29 15:39
閱讀 3062·2019-08-29 12:28
閱讀 1239·2019-08-29 11:11