<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
面試題:
vue
中created
、watch(immediate: true)
和computed
的執行順序是啥?
先看個簡單的例子:
// main.js import Vue from "vue"; new Vue({ el: "#app", template: `<div> <div>{{computedCount}}</div> </div>`, data() { return { count: 1, } }, watch: { count: { handler() { console.log('watch'); }, immediate: true, } }, computed: { computedCount() { console.log('computed'); return this.count + 1; } }, created() { console.log('created'); }, });
當前例子的執行順序為:watch
--> created
--> computed
。
在new Vue
的範例化過程中,會執行初始化方法this._init
,其中有程式碼:
Vue.prototype._init = function (options) { // ... initState(vm); // ... callHook(vm, 'created'); // ... if (vm.$options.el) { vm.$mount(vm.$options.el); } } function initState (vm) { vm._watchers = []; var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } if (opts.methods) { initMethods(vm, opts.methods); } if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } if (opts.computed) { initComputed(vm, opts.computed); } if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch); } }
猛一看程式碼,是不是發現先執行的initComputed(vm, opts.computed)
,然後執行initWatch(vm, opts.watch)
,再執行callHook(vm, 'created')
,那為啥不是computed
--> watch
--> created
呢?
不要著急,聽我娓娓道來。
const computedWatcherOptions = { lazy: true } function initComputed (vm: Component, computed: Object) { // $flow-disable-line const watchers = vm._computedWatchers = Object.create(null) // computed properties are just getters during SSR const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get // ... if (!isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (!(key in vm)) { defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV !== 'production') { // ... } } }
在通過initComputed
初始化計算屬性的時候,通過遍歷的方式去處理當前元件中的computed
。首先,在進行計算屬性範例化的時候,將{ lazy: true }
作為引數傳入,並且範例化的Watcher
中的getter
就是當前例子中的computedCount
函數;其次,通過defineComputed(vm, key, userDef)
的方式在當前元件範例vm
上為key
進行userDef
的處理。具體為:
export function defineComputed ( target: any, key: string, userDef: Object | Function ) { const shouldCache = !isServerRendering() if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef) sharedPropertyDefinition.set = noop } // ... Object.defineProperty(target, key, sharedPropertyDefinition) } function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } } }
從以上可以看出,這裡通過Object.defineProperty(target, key, sharedPropertyDefinition)
的方式,將函數computedGetter
作為get
函數,只有當對key
進行存取的時候,才會觸發其內部的邏輯。內部邏輯watcher.evaluate()
為:
evaluate () { this.value = this.get() this.dirty = false }
get
中有主要邏輯:
value = this.getter.call(vm, vm)
這裡的this.getter
就是當前例子中的:
computedCount() { console.log('computed'); return this.count + 1; }
也就是說,只有當獲取computedCount
的時候才會觸發computed
的計算,也就是在進行vm.$mount(vm.$options.el)
階段才會執行到console.log('computed')
。
function initWatch (vm: Component, watch: Object) { for (const key in watch) { const handler = watch[key] if (Array.isArray(handler)) { for (let i = 0; i < handler.length; i++) { createWatcher(vm, key, handler[i]) } } else { createWatcher(vm, key, handler) } } } function createWatcher ( vm: Component, expOrFn: string | Function, handler: any, options?: Object ) { if (isPlainObject(handler)) { options = handler handler = handler.handler } if (typeof handler === 'string') { handler = vm[handler] } return vm.$watch(expOrFn, handler, options) }
在通過initWatch
初始化偵聽器的時候,如果watch
為陣列,則遍歷執行createWatcher
,否則直接執行createWatcher
。如果handler
是物件或者字串時,將其進行處理,最終作為引數傳入vm.$watch
中去,具體為:
Vue.prototype.$watch = function ( expOrFn: string | Function, cb: any, options?: Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {} options.user = true const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { try { cb.call(vm, watcher.value) } catch (error) { handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`) } } return function unwatchFn () { watcher.teardown() } }
這裡獲取到的options
中會有immediate: true
的鍵值,同時通過options.user = true
設定user
為true
,再將其作為引數傳入去進行Watcher
的範例化。
當前例子中options.immediate
為true
,所以會執行cb.call(vm, watcher.value)
,也就是以vm
為主體,立刻執行cb
。當前例子中cb
就是handler
:
handler() { console.log('watch'); },
這裡就解釋了當前例子中console.log('watch')
是最先執行的。
然後,執行完initComputed
和initWatch
以後,就會通過callHook(vm, 'created')
執行到生命週期中的console.log('created')
了。
最後通過vm.$mount(vm.$options.el)
進行頁面渲染的時候,會先去建立vNode
,這時就需要獲取到computedCount
的值,進而觸發其get
函數的後續邏輯,最終執行到console.log('computed')
。
附:為什麼vue中的watch在mounted之後執行
首先,在呼叫mounted的時候,會進入到defineReactive函數,然後呼叫函數裡面的set方法,將mounted中賦的新的值給傳遞過去,並通過呼叫dep.notify( )把訊息傳送給訂閱者,繼而更新訂閱者的列表
後面才開始渲染watch進入Watcher的class
關於
vue
中created
和watch
的執行順序相對比較簡單,而其中computed
是通過Object.defineProperty
為當前vm
進行定義,再到後續建立vNode
階段才去觸發執行其get
函數,最終執行到計算屬性computed
對應的邏輯。
到此這篇關於vue中created、watch和computed執行順序詳解的文章就介紹到這了,更多相關vue created、watch和computed執行順序內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45