久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

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

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

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

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

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

      Watcher

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

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

      了解了入?yún)⑹怯脕砀陕锏闹?,也就基本上知?Watcher 這個(gè)對象干了啥。

      Dep

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

      Dep 主要起到一個(gè)紐帶的作用,就是連接 reactive data 與 watcher,每一個(gè) reactive data 的創(chuàng)建,都會隨著創(chuàng)建一個(gè) dep 實(shí)例。參見 observer/index.js 中的defineReactive 方法,精簡的 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í)例后,就會給該 data 注入 getter 和 setter 的邏輯,當(dāng)該 data 被引用的時(shí)候,就會觸發(fā) getter,而什么時(shí)候 data 會被引用呢?就是在 watcher 執(zhí)行 getter 的時(shí)候,而當(dāng) watcher 執(zhí)行 getter 的時(shí)候,watcher 會被塞入 Dep.target,然后通過調(diào)用 dep.depend() 方法,這個(gè)數(shù)據(jù)的 dep 就和 watcher 創(chuàng)建了連接。

      創(chuàng)建連接之后,當(dāng) data 被更改,觸發(fā)了 setter 邏輯。然后就可以通過 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 就會通知到這三個(gè) watcher 做出相應(yīng)處理。

      Observer

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

      流程

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

      在 vue 實(shí)例化的時(shí)候,會先調(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 對象,而 Observer 對象就像我上面說的,對 data 進(jìn)行遍歷,并且調(diào)用 defineReactive 方法。

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

      initComputed

      然后是 initComputed 方法。這個(gè)方法就是處理 vue 中 computed 節(jié)點(diǎn)下的數(shù)據(jù),遍歷 computed 節(jié)點(diǎn),獲取 key 和 value,創(chuàng)建 watcher 對象,如果 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 是可以為方法,也可以是字符串的。因此,通過上面的代碼我們發(fā)現(xiàn)了一種官方文檔里沒有說明的用法,比如我的 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 為空,也能保證不會報(bào)錯,類似于 lodash.get 的用法。OK,扯遠(yuǎn)了,回到正題上。

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

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

      前面我有說過,computed data 的 watcher 是 lazy 的,當(dāng) computed data 中引用的 data 發(fā)生改變后,是不會立馬重新計(jì)算值的,而只是標(biāo)記一下 dirty 為 true,然后當(dāng)這個(gè) computed data 被引用的時(shí)候,上面的 getter 邏輯就會判斷 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í)候,就會立馬執(zhí)行 render function 了,當(dāng)執(zhí)行 render function 的時(shí)候。如果 template 中有使用 data,則會觸發(fā) data 的 getter 邏輯,然后執(zhí)行 dep.depend() 進(jìn)行依賴收集,如果 template 中有使用 computed 的參數(shù),也會觸發(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號-2    滬公網(wǎng)安備31011702889846號