亚洲最大看欧美片,亚洲图揄拍自拍另类图片,欧美精品v国产精品v呦,日本在线精品视频免费

  • 站長(zhǎng)資訊網(wǎng)
    最全最豐富的資訊網(wǎng)站

    解讀Vue之怎樣把數(shù)據(jù)包裝成reactive從而實(shí)現(xiàn)MDV效果

    本篇文章就來(lái)講講 vue 是如何把數(shù)據(jù)包裝成 reactive,從而實(shí)現(xiàn) MDV(Model-Driven-View) 的效果,希望對(duì)大家有幫助。

    解讀Vue之怎樣把數(shù)據(jù)包裝成reactive從而實(shí)現(xiàn)MDV效果先說(shuō)明一下什么叫 reactive,簡(jiǎn)單來(lái)說(shuō),就是將數(shù)據(jù)包裝成一種可觀測(cè)的類型,當(dāng)數(shù)據(jù)產(chǎn)生變更的時(shí)候,我們能夠感知到。

    而 Vue 的相關(guān)實(shí)現(xiàn)代碼全部都在 core/observer 目錄下,而要自行閱讀的話,建議從 core/instance/index.js 中開(kāi)始。

    在開(kāi)始講 reactive 的具體實(shí)現(xiàn)之前,先說(shuō)說(shuō)幾個(gè)對(duì)象:Watcher、Dep、Observer。

    Watcher

    Watcher 是 vue 實(shí)現(xiàn)的一個(gè)用于觀測(cè)數(shù)據(jù)的對(duì)象,具體實(shí)現(xiàn)在 core/observer/watcher.js 中。

    這個(gè)類主要是用來(lái)觀察方法/表達(dá)式中引用到的數(shù)據(jù)(數(shù)據(jù)需要是 reative 的,即 data 或者 props)變更,當(dāng)變更后做出相應(yīng)處理。先看一下 Watcher 這個(gè)類的入?yún)ⅲ?/p>

    vm: Component,expOrFn: string | Function,cb: Function,options?: Object

    解釋一下這幾個(gè)入?yún)⑹歉陕锏模?/p>

    • vm:當(dāng)前這個(gè) watcher 所屬的 VueComponent。
    • expOrFn:需要監(jiān)聽(tīng)的 方法/表達(dá)式。舉個(gè)例子:VueComponent 的 render function,或者是 computed 的 getter 方法,再或者是abc.bbc.aac這種類型的字符串(由于 vue 的 parsePath 方法是用 split('.') 來(lái)做的屬性分割,所以不支持abc['bbc'])。expOrFn 如果是方法,則直接賦值給 watcher 的 getter 屬性,如果是表達(dá)式,則會(huì)轉(zhuǎn)換成方法再給 getter。
    • cb:當(dāng) getter 中引用到的 data 發(fā)生改變的時(shí)候,就會(huì)觸發(fā)該回調(diào)。
    • options:額外參數(shù),可以傳入的參數(shù)為包括deep、userlazy,sync,這些值默認(rèn)都是為 false。
      • deep 如果為 true,會(huì)對(duì) getter 返回的對(duì)象再做一次深度遍歷,進(jìn)行進(jìn)一步的依賴收集。
      • user 是用于標(biāo)記這個(gè)監(jiān)聽(tīng)是否由用戶通過(guò) $watch 調(diào)用的。
      • lazy 用于標(biāo)記 watcher 是否為懶執(zhí)行,該屬性是給 computed data 用的,當(dāng) data 中的值更改的時(shí)候,不會(huì)立即計(jì)算 getter 獲取新的數(shù)值,而是給該 watcher 標(biāo)記為dirty,當(dāng)該 computed data 被引用的時(shí)候才會(huì)執(zhí)行從而返回新的 computed data,從而減少計(jì)算量。
      • sync 則是表示當(dāng) data 中的值更改的時(shí)候,watcher 是否同步更新數(shù)據(jù),如果是 true,就會(huì)立即更新數(shù)值,否則在 nextTick 中更新。

    了解了入?yún)⑹怯脕?lái)干嘛的之后,也就基本上知道 Watcher 這個(gè)對(duì)象干了啥。

    Dep

    Dep 則是 vue 實(shí)現(xiàn)的一個(gè)處理依賴關(guān)系的對(duì)象,具體實(shí)現(xiàn)在 core/observer/dep.js 中,代碼量相當(dāng)少,很容易理解。

    Dep 主要起到一個(gè)紐帶的作用,就是連接 reactive data 與 watcher,每一個(gè) reactive data 的創(chuàng)建,都會(huì)隨著創(chuàng)建一個(gè) dep 實(shí)例。參見(jiàn) observer/index.js 中的defineReactive 方法,精簡(jiǎn)的 defineReactive 方法如下。

    function defineReactive(obj, key, value) {     const dep = new Dep();     Object.defineProperty(obj, key, {         get() {           if (Dep.target) {             dep.depend();           }           return value        }         set(newValue) {             value = newValue;             dep.notify();         }     })}

    創(chuàng)建完 dep 實(shí)例后,就會(huì)給該 data 注入 getter 和 setter 的邏輯,當(dāng)該 data 被引用的時(shí)候,就會(huì)觸發(fā) getter,而什么時(shí)候 data 會(huì)被引用呢?就是在 watcher 執(zhí)行 getter 的時(shí)候,而當(dāng) watcher 執(zhí)行 getter 的時(shí)候,watcher 會(huì)被塞入 Dep.target,然后通過(guò)調(diào)用 dep.depend() 方法,這個(gè)數(shù)據(jù)的 dep 就和 watcher 創(chuàng)建了連接。

    創(chuàng)建連接之后,當(dāng) data 被更改,觸發(fā)了 setter 邏輯。然后就可以通過(guò) dep.notify() 通知到所有與 dep 創(chuàng)建了關(guān)聯(lián)的 watcher。從而讓各個(gè) watcher 做出響應(yīng)。

    比如我 watch 了一個(gè) data ,并且在一個(gè) computed data 中引用了同一個(gè) data。再同時(shí),我在 template 中也有顯式引用了這個(gè) data,那么此時(shí),這個(gè) data 的 dep 里就關(guān)聯(lián)了三個(gè) watcher,一個(gè)是 render function 的 watcher,一個(gè)是 computed 的 watcher,一個(gè)是用戶自己調(diào)用 $watch 方法創(chuàng)建的 watcher。當(dāng) data 發(fā)生更改后,這個(gè) data 的 dep 就會(huì)通知到這三個(gè) watcher 做出相應(yīng)處理。

    Observer

    Observer 可以將一個(gè) plainObject 或者 array 變成 reactive 的。代碼很少,就是遍歷 plainObject 或者 array,對(duì)每一個(gè)鍵值調(diào)用defineReactive 方法。

    流程

    以上三個(gè)類介紹完了,基本上對(duì) vue reactive 的實(shí)現(xiàn)應(yīng)該有個(gè)模糊的認(rèn)識(shí),接下來(lái),就結(jié)合實(shí)例講一下整個(gè)流程。

    在 vue 實(shí)例化的時(shí)候,會(huì)先調(diào)用 initData,再調(diào)用 initComputed,最后再調(diào)用 mountComponent 創(chuàng)建 render function 的 watcher。從而完成一個(gè) VueComponent 的數(shù)據(jù) reactive 化。

    initData

    initData 方法在 core/instance/state.js 中,而這個(gè)方法里大部分都是做一些判斷,比如防止 data 里有跟 methods 里重復(fù)的命名之類的。核心其實(shí)就一行代碼:

    observe(data, true)

    而這個(gè) observe 方法干的事就是創(chuàng)建一個(gè) Observer 對(duì)象,而 Observer 對(duì)象就像我上面說(shuō)的,對(duì) data 進(jìn)行遍歷,并且調(diào)用 defineReactive 方法。

    就會(huì)使用 data 節(jié)點(diǎn)創(chuàng)建一個(gè) Observer 對(duì)象,然后對(duì) data 下的所有數(shù)據(jù),依次進(jìn)行 reactive 的處理,也就是調(diào)用 defineReactive 方法。當(dāng)執(zhí)行完 defineReactive 方法之后,data 里的每一個(gè)屬性,都被注入了 getter 以及 setter 邏輯,并且創(chuàng)建了 dep 對(duì)象。至此 initData 執(zhí)行完畢。

    initComputed

    然后是 initComputed 方法。這個(gè)方法就是處理 vue 中 computed 節(jié)點(diǎn)下的數(shù)據(jù),遍歷 computed 節(jié)點(diǎn),獲取 key 和 value,創(chuàng)建 watcher 對(duì)象,如果 value 是方法,實(shí)例化 watcher 的入?yún)?expOrFn 則為 value,否則是 value.get。

    function initComputed (vm: Component, computed: Object) {   ...  const watchers = vm._computedWatchers = Object.create(null)  for (const key in computed) {     const userDef = computed[key]    let getter = typeof userDef === 'function' ? userDef : userDef.get     ...     watchers[key] = new Watcher(vm, getter, noop, { lazy: true })    if (!(key in vm)) {       defineComputed(vm, key, userDef)    } else if (process.env.NODE_ENV !== 'production') {       ...    }   }}

    我們知道 expOrFn 是可以為方法,也可以是字符串的。因此,通過(guò)上面的代碼我們發(fā)現(xiàn)了一種官方文檔里沒(méi)有說(shuō)明的用法,比如我的 data 結(jié)構(gòu)如下

    { obj: { list: [{value: '123'}] } }

    如果我們要在 template 中需要使用 list 中第一個(gè)節(jié)點(diǎn)的 value 屬性 值,就寫個(gè) computed:

    computed: {   value: { get: 'obj.list.0.value' }}

    然后在 template 中使用的時(shí)候,直接用{{ value }},這樣的話,就算 list 為空,也能保證不會(huì)報(bào)錯(cuò),類似于 lodash.get 的用法。OK,扯遠(yuǎn)了,回到正題上。

    創(chuàng)建完 watcher,就通過(guò) Object.defineProperty 把 computed 的 key 掛載到 vm 上。并且在 getter 中添加以下邏輯

     if (watcher.dirty) {    watcher.evaluate() }  if (Dep.target) {    watcher.depend() }  return watcher.value

    前面我有說(shuō)過(guò),computed data 的 watcher 是 lazy 的,當(dāng) computed data 中引用的 data 發(fā)生改變后,是不會(huì)立馬重新計(jì)算值的,而只是標(biāo)記一下 dirty 為 true,然后當(dāng)這個(gè) computed data 被引用的時(shí)候,上面的 getter 邏輯就會(huì)判斷 watcher 是否為 dirty,如果是,就重新計(jì)算值。

    而后面那一段watcher.depend。則是為了收集 computed data 中用到的 data 的依賴,從而能夠?qū)崿F(xiàn)當(dāng) computed data 中引用的 data 發(fā)生更改時(shí),也能觸發(fā)到 render function 的重新執(zhí)行。

      depend () {     let i = this.deps.length     while (i--) {       this.deps[i].depend()    }   }

    mountComponent

    把 data 以及 computed 都初始化好之后,則創(chuàng)建一個(gè) render function 的 watcher。邏輯如下:

    export function mountComponent (   vm: Component,   el: ?Element,   hydrating?: boolean): Component {   vm.$el = el   ...  callHook(vm, 'beforeMount')  let updateComponent   ...     updateComponent = () => {       vm._update(vm._render(), hydrating)    }   ...  vm._watcher = new Watcher(vm, updateComponent, noop)  if (vm.$vnode == null) {     vm._isMounted = true     callHook(vm, 'mounted')  }   return vm}

    可以看到,創(chuàng)建 watcher 時(shí)候的入?yún)?expOrFn 為 updateComponent 方法,而 updateComponent 方法中則是執(zhí)行了 render function。而這個(gè) watcher 不是 lazy 的,因此創(chuàng)建該 watcher 的時(shí)候,就會(huì)立馬執(zhí)行 render function 了,當(dāng)執(zhí)行 render function 的時(shí)候。如果 template 中有使用 data,則會(huì)觸發(fā) data 的 getter 邏輯,然后執(zhí)行 dep.depend() 進(jìn)行依賴收集,如果 template 中有使用 computed 的參數(shù),也會(huì)觸發(fā) computed 的 getter 邏輯,從而再收集 computed 的方法中引用的 data 的依賴。最終完成全部依賴的收集。

    最后舉個(gè)例子:

    <template>     <p>{{ test }}</p></template><script>   export default {     data() {       return {         name: 'cool'       }     },     computed: {       test() {         return this.name + 'test';       }     }   }</script>

    初始化流程:

    1. 將 name 處理為 reactive,創(chuàng)建 dep 實(shí)例
    2. 將 test 綁到 vm,創(chuàng)建 test 的 watcher 實(shí)例 watch1,添加 getter 邏輯。
    3. 創(chuàng)建 render function 的 watcher 實(shí)例 watcher2,并且立即執(zhí)行 render function。
    4. 執(zhí)行 render function 的時(shí)候,觸發(fā)到 test 的 getter 邏輯,watcher1 及 watcher2 均與 dep 創(chuàng)建映射關(guān)系。

    name 的值變更后的更新流程:

    1. 遍歷綁定的 watcher 列表,執(zhí)行 watcher.update()。
    2. watcher1.dirty 置為為 true。
    3. watcher2 重新執(zhí)行 render function,觸發(fā)到 test 的 getter,因?yàn)?watcher1.dirty 為 true,因此重新計(jì)算 test 的值,test 的值更新。
    4. 重渲染 view

    贊(0)
    分享到: 更多 (0)
    網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)