首頁  >  文章  >  web前端  >  vue響應式的原理是什麼? vue響應式原理的分析

vue響應式的原理是什麼? vue響應式原理的分析

不言
不言原創
2018-09-10 15:33:512653瀏覽

這篇文章帶給大家的內容是關於vue響應式的原理是什麼? vue響應式原理的分析,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

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進行呼叫對應的初始化函數

initProps與initData

#主要工作是呼叫defineProperty給屬性分別掛載get(觸發該鉤子時,會將目前屬性的dep實例推入當前的Dep.target也就是當前watcher的deps中即它訂閱的依賴,Dep.target下文會講到。且該dep實例也會將當前watcher即觀察者推入其subs數組中)、set方法(通知該依賴subs中所有的觀察者watcher去呼叫他們的update方法)。

initComputed

它的作用是將computed物件中所有的屬性遍歷,並給該屬性new一個computed watcher(計算屬性中定義了個dep依賴,給需要使用該計算屬性的watcher訂閱)。也會透過呼叫defineProperty給computed掛載get(get方法)、set方法(set方法會判斷是否傳入,如果沒傳入會設定成noop空函數)
computed屬性的get方法是下面函數的回傳值函數

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(),該方法是透過呼叫watcher的get方法(其中需要注意的是watcher的get方法會呼叫pushTarget將先前的Dep.target實例入棧,並設定Dep.target為該computed watcher,被該計算屬性所依賴的響應式屬性會將該computed watcher推入其subs中,所以當被依賴的響應式屬性改變時,會通知訂閱他的computed watcher,computed watcher 再通知訂閱該計算屬性的watcher呼叫update方法),get方法中呼叫計算屬性key綁定的handler函數計算出值。

initWatch

該watcher 為user watcher(開發人員自己在元件中自訂的)。
initWatch的作用是遍歷watch中的屬性,並對每個watch監聽的屬性呼叫定義的$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()
    }
  }

程式碼中呼叫new Watcher的時候,也會同render watcher一樣,執行下watcher的get方法,呼叫pushTarget將當前user watcher賦值給Dep.target,get()中value = this.getter.call(vm, vm)這個語句會觸發該自訂watcher監聽的響應式屬性的get方法,並將當前的user watcher推入該屬性所依賴的subs中,所以當user watcher監聽的屬性set觸發後,通知訂閱該依賴的watcher去觸發update,也就是觸發該watch綁定的key對應的handler。然後就是呼叫popTarget出棧並賦值給Dep.target。

$mount

initState初始化工作大致到這裡過,接下去會執行$mount開始渲染工作
$mount主要工作:new了一個渲染Watcher,並將updateCompent作為callback傳遞進去並執行

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不會一開始就執行它的get()方法。 $mount裡面new的這個render watcher會呼叫get()方法,呼叫pushTarget將目前render watcher賦值給Dep.target。接下去重頭戲來了,呼叫updateComponent,該方法會執行vm._update(vm._render(), hydrating),其中render函數會觸發html中使用到的響應式屬性的get鉤子。 get鉤子會讓該響應式屬性的依賴實例dep將目前的render watcher推入其subs數組中,所以當依賴的響應式屬性改變之後,會遍歷subs通知訂閱它的watcher去調用update()。

範例

可能大家對watcher和dep調來調去一頭霧水,我講個實例

<div id="app">
      <div>{{a}}</div>
      <div>{{b}}</div>
    </div>
new Vue({
  el: "#app",
  data() {
    return {
      a:1,
    }
  },
  computed:{
    b() {
      return a+1
    }
  },
})

我直接從渲染開始講,只講跟dep跟watcher有關的
$mount:new一個渲染watcher(watcher的get方法中會將渲染watcher賦值給Dep.target)的時候會觸發vm._update(vm._render(), hydrating),render的時候會取得html中用到的響應式屬性,上面例子中先用到了a,這時會觸發a的get鉤子,其中dep.depend()會將當前的渲染watcher推入到a屬性的dep的subs數組中。

接著繼續執行,訪問到b(b是計算屬性的值),會觸發計算屬性的get方法。計算屬性的get方法是呼叫createComputedGetter函數後的回傳函數computedGetter,computedGetter函數中會執行watcher.depend()。 Watcher的depend方法是特別留給computed watcher使用的。

剛才上面說過了除了computed watcher,其他兩種watcher在new 完之後都會執行他們的get方法,那麼computed watcher在new完之後幹嘛呢,它會new一個dep。

回到剛才說的專門為computed watcher開設的方法watcher.depend(),他的作用是執行this.dep.depend()(computed watcher定義的dep就是在這裡使用到的)。 this.dep.depend()會讓目前的渲染watcher訂閱該運算屬性依賴,該計算屬性也會將渲染watcher推入到它自己的subs([render watcher])中,當計算屬性的值修改之後會通知subs中的watcher呼叫update(),所以計算屬性值變了頁面能刷新。

回到前面說的觸發b計算屬性的get鉤子那裡,get鉤子最後會執行watcher.evaluate(),watcher.evaluate()會執行computed watcher的get()方法。

這時重點來了,會將Dep.target(render watcher)推入targetStack堆疊中(存入之後以便待會兒取出繼續用),然後將這個計算屬性的computed watcher賦值給Dep .target。 get方法中value = this.getter.call(vm, vm),會執行computed屬性綁定的handler。

如上範例return a 1。使用了a那麼就一定會觸發a的get鉤子,get鉤子又會調用dep.depend(),dep.depend()會讓computed watcher將dep存入它的deps數組中,a的dep會將當前的Dep.target(computed watcher)存入其subs數組中,當前例子中a的subs中就會是[render watcher,computed watcher],所以a值變化會遍歷a的subs中的watcher調用update()方法, html中用到的a會刷新,計算屬性watcher呼叫update()方法會通知他自己的subs([render watcher])中render watcher去呼叫update方法,html中用到的計算屬性b才會刷新dom(這裡提個醒,我只是粗略的講,計算屬性依賴的屬性變化後他不一定會觸發更新,他會比較計算完之後的值是否變化)。

computed watcher的get()方法最後會呼叫popTarget(),將之前存入render watcher出棧並賦值給Dep.target,這時候我例子中targetStack就變成空數組了。

render watcher的get方法執行到最後也會出棧,這時候會將Dep.target賦值會空。

相關推薦:

Vue資料響應式原理分析

#在Vue中有關響應式原理(詳細教學)


#

以上是vue響應式的原理是什麼? vue響應式原理的分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

相關文章

看更多