摘要:響應(yīng)式原理判斷該實例是否存在進行調(diào)用相應(yīng)的初始化函數(shù)與主要工作是調(diào)用給屬性分別掛載觸發(fā)該鉤子時,會將當前屬性的實例推入當前的也就是當前的中即它訂閱的依賴,下文會講到。
vue響應(yīng)式原理 initState
new Vue() => _init() => initState:
function initState (vm: Component) { vm._watchers = [] const 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) } }
判斷該vue實例是否存在props、methods、data、computed、watch進行調(diào)用相應(yīng)的初始化函數(shù)
initProps與initData主要工作是調(diào)用defineProperty給屬性分別掛載get(觸發(fā)該鉤子時,會將當前屬性的dep實例推入當前的Dep.target也就是當前watcher的deps中即它訂閱的依賴,Dep.target下文會講到。且該dep實例也會將當前watcher即觀察者推入其subs數(shù)組中)、set方法(通知該依賴subs中所有的觀察者watcher去調(diào)用他們的update方法)。
initComputed它的作用是將computed對象中所有的屬性遍歷,并給該屬性new一個computed watcher(計算屬性中定義了個dep依賴,給需要使用該計算屬性的watcher訂閱)。也會通過調(diào)用defineProperty給computed掛載get(get方法)、set方法(set方法會判斷是否傳入,如果沒傳入會設(shè)置成noop空函數(shù))
computed屬性的get方法是下面函數(shù)的返回值函數(shù)
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
注意其中的watcher.depend(),該方法讓用到該屬性的watcher觀察者訂閱該watcher中的依賴,且該計算屬性watcher會將訂閱它的watcher推入他的subs中(當計算屬性值改變的時候,通知訂閱他的watcher觀察者)
watcher.evaluate(),該方法是通過調(diào)用watcher的get方法(其中需要注意的是watcher的get方法會調(diào)用pushTarget將之前的Dep.target實例入棧,并設(shè)置Dep.target為該computed watcher,被該計算屬性依賴的響應(yīng)式屬性會將該computed watcher推入其subs中,所以當被依賴的響應(yīng)式屬性改變時,會通知訂閱他的computed watcher,computed watcher 再通知訂閱該計算屬性的watcher調(diào)用update方法),get方法中調(diào)用計算屬性key綁定的handler函數(shù)計算出值。
該watcher 為user watcher(開發(fā)人員自己在組件中自定義的)。
initWatch的作用是遍歷watch中的屬性,并對每個watch監(jiān)聽的屬性調(diào)用定義的$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 // 代表該watcher是用戶自定義watcher const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { cb.call(vm, watcher.value) } return function unwatchFn () { watcher.teardown() } }
代碼中調(diào)用new Watcher的時候,也會同render watcher一樣,執(zhí)行下watcher的get方法,調(diào)用pushTarget將當前user watcher賦值給Dep.target,get()中value = this.getter.call(vm, vm)這個語句會觸發(fā)該自定義watcher監(jiān)聽的響應(yīng)式屬性的get方法,并將當前的user watcher推入該屬性依賴的subs中,所以當user watcher監(jiān)聽的屬性set觸發(fā)后,通知訂閱該依賴的watcher去觸發(fā)update,也就是觸發(fā)該watch綁定的key對應(yīng)的handler。然后就是調(diào)用popTarget出棧并賦值給Dep.target。
$mountinitState初始化工作大致到這里過,接下去會執(zhí)行$mount開始渲染工作
$mount主要工作:new了一個渲染W(wǎng)atcher,并將updateCompent作為callback傳遞進去并執(zhí)行
updateComponent = () => { vm._update(vm._render(), hydrating) } new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted) { callHook(vm, "beforeUpdate") } } }, true /* isRenderWatcher */)
三種watcher中new Watcher的時候,只有computed watcher不會一開始就執(zhí)行它的get()方法。$mount里面new的這個render watcher會調(diào)用get()方法,調(diào)用pushTarget將當前render watcher賦值給Dep.target。接下去重頭戲來了,調(diào)用updateComponent,該方法會執(zhí)行vm._update(vm._render(), hydrating),其中render函數(shù)會觸發(fā)html中使用到的響應(yīng)式屬性的get鉤子。get鉤子會讓該響應(yīng)式屬性的依賴實例dep將當前的render watcher推入其subs數(shù)組中,所以當依賴的響應(yīng)式屬性改變之后,會遍歷subs通知訂閱它的watcher去調(diào)用update()。
例子可能大家對watcher和dep調(diào)來調(diào)去一頭霧水,我講個實例
new Vue({ el: "#app", data() { return { a:1, } }, computed:{ b() { return a+1 } }, }){{a}}{{b}}
我直接從渲染開始講,只講跟dep跟watcher有關(guān)的
$mount:new一個渲染watcher(watcher的get方法中會將渲染watcher賦值給Dep.target)的時候會觸發(fā) vm._update(vm._render(), hydrating),render的時候會獲取html中用到的響應(yīng)式屬性,上面例子中先用到了a,這時會觸發(fā)a的get鉤子,其中dep.depend()會將當前的渲染watcher推入到a屬性的dep的subs數(shù)組中。
接下去繼續(xù)執(zhí)行,訪問到b(b是計算屬性的值),會觸發(fā)計算屬性的get方法。計算屬性的get方法是調(diào)用createComputedGetter函數(shù)后的返回函數(shù)computedGetter,computedGetter函數(shù)中會執(zhí)行watcher.depend()。Watcher的depend方法是專門留給computed watcher使用的。剛才上面說過了除了computed watcher,其他兩種watcher在new 完之后都會執(zhí)行他們的get方法,那么computed watcher在new完之后干嘛呢,它會new一個dep。回到剛才說的專門為computed watcher開設(shè)的方法watcher.depend(),他的作用是執(zhí)行this.dep.depend()(computed watcher定義的dep就是在這里使用到的)。this.dep.depend()會讓當前的渲染watcher訂閱該計算屬性依賴,該計算屬性也會將渲染watcher推入到它自己的subs([render watcher])中,當計算屬性的值修改之后會通知subs中的watcher調(diào)用update(),所以計算屬性值變了頁面能刷新。回到前面說的觸發(fā)b計算屬性的get鉤子那里,get鉤子最后會執(zhí)行watcher.evaluate(),watcher.evaluate()會執(zhí)行computed watcher的get()方法。這時候重點來了,會將Dep.target(render watcher)推入targetStack棧中(存入之后以便待會兒取出繼續(xù)用),然后將這個計算屬性的computed watcher賦值給Dep.target。get方法中value = this.getter.call(vm, vm),會執(zhí)行computed屬性綁定的handler。如上面例子中return a + 1。使用了a那么就一定會觸發(fā)a的get鉤子,get鉤子又會調(diào)用dep.depend(),dep.depend()會讓computed watcher將dep存入它的deps數(shù)組中,a的dep會將當前的Dep.target(computed watcher)存入其subs數(shù)組中,當前例子中a的subs中就會是[render watcher,computed watcher],所以a值變化會遍歷a的subs中的watcher調(diào)用update()方法,html中用到的a會刷新,計算屬性watcher調(diào)用update()方法會通知他自己的subs([render watcher])中render watcher去調(diào)用update方法,html中用到的計算屬性b才會刷新dom(這里提個醒,我只是粗略的講,計算屬性依賴的屬性變化后他不一定會觸發(fā)更新,他會比較計算完之后的值是否變化)。computed watcher的get()方法最后會調(diào)用popTarget(),將之前存入render watcher出棧并賦值給Dep.target,這時候我例子中targetStack就變成空數(shù)組了。render watcher的get方法執(zhí)行到最后也會出棧,這時候會將Dep.target賦值會空。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97626.html
摘要:問題為什么修改即可觸發(fā)更新和的關(guān)聯(lián)關(guān)系官方介紹的官網(wǎng)文檔,對響應(yīng)式屬性的原理有一個介紹。因此本文在源碼層面,對響應(yīng)式原理進行梳理,對關(guān)鍵步驟進行解析。 描述 ?我們通過一個簡單的 Vue應(yīng)用 來演示 Vue的響應(yīng)式屬性: html: {{message}} js: let vm = new Vue({ el: #ap...
摘要:在讀取訪問器屬性時,就會調(diào)用函數(shù),該函數(shù)負責返回有效的值在寫入訪問器屬性時,會調(diào)用函數(shù)并傳入新值,該函數(shù)負責決定如何處理數(shù)據(jù),但是這兩個函數(shù)不一定非要同時存在。 前言 Vue最明顯的特性之一便是它的響應(yīng)式系統(tǒng),其數(shù)據(jù)模型即是普通的 JavaScript 對象。而當你讀取或?qū)懭胨鼈儠r,視圖便會進行響應(yīng)操作。文章簡要闡述下其實現(xiàn)原理,如有錯誤,還請不吝指正。個人博客鏈接:hiybm.cn ...
摘要:所以我今后打算把每一個內(nèi)容分成白話版和源碼版。有什么錯誤的地方,感謝大家能夠指出響應(yīng)式系統(tǒng)我們都知道,只要在實例中聲明過的數(shù)據(jù),那么這個數(shù)據(jù)就是響應(yīng)式的。什么是響應(yīng)式,也即是說,數(shù)據(jù)發(fā)生改變的時候,視圖會重新渲染,匹配更新為最新的值。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 V...
寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】Props - 源碼版 今天記錄 Props 源碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。 幸好我做...
摘要:原型方法通過原型方法方法來掛載實例。當響應(yīng)式屬性發(fā)生變化時,會通知依賴列表中的對象進行更新。此時,對象執(zhí)行方法,重新渲染節(jié)點。在執(zhí)行過程中,如果需要讀取響應(yīng)式屬性,則會觸發(fā)響應(yīng)式屬性的。總結(jié)響應(yīng)式屬性的原理 vue實例 初始化 完成以后,接下來就要進行 掛載。 vue實例掛載,即為將vue實例對應(yīng)的 template模板,渲染成 Dom節(jié)點。 原型方法 - $mount ? 通過原...
摘要:對象用戶看到的對象用戶看到的是這個對象即是實際使用的對象實際使用的對象復(fù)制更新相應(yīng)的代碼實現(xiàn)對象代理響應(yīng)式原理前提官網(wǎng)說過,限于現(xiàn)代瀏覽器限制,無法監(jiān)測通過這種方式添加的屬性,所以,他的響應(yīng)式是建立在實例化對象的時候,預(yù)定義屬性的基礎(chǔ)上的。 1. Vue 對象 1.1 用戶看到的對象 var app = new Vue({ el: #app , /* * 用...
閱讀 2579·2021-08-20 09:38
閱讀 1360·2019-08-30 15:43
閱讀 597·2019-08-29 17:13
閱讀 1607·2019-08-29 14:01
閱讀 1319·2019-08-29 13:29
閱讀 2327·2019-08-23 18:29
閱讀 2051·2019-08-23 17:51
閱讀 1920·2019-08-23 17:16