成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

淺析Vue響應(yīng)式原理(二)

rockswang / 2238人閱讀

摘要:響應(yīng)式原理之之前簡(jiǎn)單介紹了和類的代碼和作用,現(xiàn)在來(lái)介紹一下類和。對(duì)于數(shù)組,響應(yīng)式的實(shí)現(xiàn)稍有不同。不存在時(shí),說(shuō)明不是響應(yīng)式數(shù)據(jù),直接更新。如果對(duì)象是響應(yīng)式的,確保刪除能觸發(fā)更新視圖。

Vue響應(yīng)式原理之Observer

之前簡(jiǎn)單介紹了Dep和Watcher類的代碼和作用,現(xiàn)在來(lái)介紹一下Observer類和set/get。在Vue實(shí)例后再添加響應(yīng)式數(shù)據(jù)時(shí)需要借助Vue.set/vm.$set方法,這兩個(gè)方法內(nèi)部實(shí)際上調(diào)用了set方法。而Observer所做的就是將修改反映到視圖中。

Observer
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0

    def(value, "__ob__", this)
    if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  observeArray (items: Array) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

Observer有三個(gè)屬性。value是響應(yīng)式數(shù)據(jù)的值;dep是Dep實(shí)例,這個(gè)Dep實(shí)例用于Vue.set/vm.$set中通知依賴更新;vmCount表示把這個(gè)數(shù)據(jù)當(dāng)成根data對(duì)象的實(shí)例數(shù)量,大于0時(shí)是實(shí)例化傳入的根data對(duì)象。

構(gòu)造函數(shù)接受一個(gè)值,表示要觀察的值,這樣,在Observer實(shí)例中引用了響應(yīng)式數(shù)據(jù),并將響應(yīng)式數(shù)據(jù)的__ob__屬性指向自身。如果被觀察值是除數(shù)組以外的類型,會(huì)調(diào)用walk方法,令每個(gè)屬性都是響應(yīng)式。對(duì)于基本類型的值,Object.keys會(huì)返回一個(gè)空數(shù)組,所以在walk內(nèi),defineReactive只在對(duì)象的屬性上執(zhí)行。如果是被觀察值是數(shù)組,那么會(huì)在每個(gè)元素上調(diào)用工廠函數(shù)observe,使其響應(yīng)式。

對(duì)于數(shù)組,響應(yīng)式的實(shí)現(xiàn)稍有不同。回顧一下在教程數(shù)組更新檢測(cè)里的說(shuō)明,變異方法會(huì)觸發(fā)視圖更新。其具體實(shí)現(xiàn)就在這里。arrayMethods是一個(gè)對(duì)象,保存了Vue重寫(xiě)的數(shù)組方法,具體重寫(xiě)方式下面再說(shuō),現(xiàn)在只需知道這些重寫(xiě)的數(shù)組方法除了保持原數(shù)組方法的功能外,還能通知依賴數(shù)據(jù)已更新。augment的用途是令value能夠調(diào)用在arrayMethods中的方法,實(shí)現(xiàn)的方式有兩種。第一種是通過(guò)原型鏈實(shí)現(xiàn),在value.__proto__添加這些方法,優(yōu)先選擇這種實(shí)現(xiàn)。部分瀏覽器不支持__proto__,則直接在value上添加這些方法。

最后執(zhí)行observeArray方法,遍歷value,在每個(gè)元素上執(zhí)行observe方法。

數(shù)組變異方法的實(shí)現(xiàn)

執(zhí)行變異方法會(huì)觸發(fā)視圖功能,所以變異方法要實(shí)現(xiàn)的功能,除了包括原來(lái)數(shù)組方法的功能外,還要有通知依賴數(shù)據(jù)更新的功能。代碼保存在/src/core/observer/array.js。

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse"
]
methodsToPatch.forEach(function (method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case "push":
      case "unshift":
        inserted = args
        break
      case "splice":
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

模塊內(nèi),使用arrayProto保存數(shù)組原型,arrayMethods的原型是arrayProto,用來(lái)保存變異后的方法,methodsToPatch是保存變異方法名的數(shù)組。

遍歷methodsToPatch,根據(jù)方法名來(lái)獲取在arrayProto上的數(shù)組變異方法,然后在arrayMethods實(shí)現(xiàn)同名方法。

在該同名方法內(nèi),首先執(zhí)行緩存的數(shù)組方法original,執(zhí)行上下文是this,這些方法最終會(huì)添加到響應(yīng)式數(shù)組或其原型上,所以被調(diào)用時(shí)this是數(shù)組本身。ob指向this.__ob__,使用inserted指向被插入的元素,調(diào)用ob.observeArray觀察新增的數(shù)組元素。最后執(zhí)行ob.dep.notify(),通知依賴更新。

observe

工廠函數(shù),獲取value上__ob__屬性指向的Observer實(shí)例,如果需要該屬性且未定義時(shí),根據(jù)數(shù)據(jù)創(chuàng)建一個(gè)Observer實(shí)例,在實(shí)例化時(shí)會(huì)在value上添加__ob__屬性。參數(shù)二表示傳入的value是否是根data對(duì)象。只有根數(shù)據(jù)對(duì)象的__ob__.vmCount大于0。

isObject判斷value是不是Object類型,實(shí)現(xiàn)如obj !== null && typeof obj === "object"。

export function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

此處可以看出,value與Observer實(shí)例ob之間是雙向引用。value.__ob__指向ob,ob.value指向value。

Vue.set

在Vue實(shí)例化以后,如果想為其添加新的響應(yīng)式屬性,對(duì)于對(duì)象,直接使用字面量賦值是沒(méi)有效果的。由響應(yīng)式數(shù)據(jù)的實(shí)現(xiàn)可以想到,這種直接賦值的方式,并沒(méi)有為該屬性自定義getter/setter,在獲取屬性時(shí)不會(huì)收集依賴,在更新屬性時(shí)不會(huì)觸發(fā)更新。如果想要為已存在的響應(yīng)式數(shù)據(jù)添加新屬性,可以使用Vue.set/vm.$set方法,但要注意,不能在data上添加新屬性。

Vue.set/vm.$set內(nèi)部都是在/src/code/observer/index.js定義的set的函數(shù)。

set函數(shù)接受三個(gè)參數(shù),參數(shù)一target表示要新增屬性的對(duì)象,參數(shù)二key表示新增的屬性名或索引,參數(shù)三val表示新增屬性的初始值。

export function set (target: Array | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== "production" &&
    !Array.isArray(target) &&
    !isObject(target)
  ) {
    warn(`Cannot set reactive property on non-object/array value: ${target}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }

  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== "production" && warn(
      "Avoid adding reactive properties to a Vue instance or its root $data " +
      "at runtime - declare it upfront in the data option."
    )
    return val
  }
  // 不存在ob 說(shuō)明不是響應(yīng)式數(shù)據(jù)
  if (!ob) {
    target[key] = val
    return val
  }
  // 為target添加新屬性
  defineReactive(ob.value, key, val)
  // ob.dep實(shí)際是target.__ob__.dep
  ob.dep.notify()
  return val
}

函數(shù)內(nèi)部首先判斷target類型,非數(shù)組或非對(duì)象的目標(biāo)數(shù)據(jù)是無(wú)法添加響應(yīng)式數(shù)據(jù)的。

如果是數(shù)組,且key是有效的數(shù)組索引,更新數(shù)組長(zhǎng)度,然后調(diào)用變異方法splice,更新對(duì)應(yīng)的值并觸發(fā)視圖更新。如果是對(duì)象,且屬性keytarget的原型鏈上且不在Object.prototype上(即不是Object原型上定義的屬性或方法),直接在target上添加或更新key

ob指向target.__ob__,如果target是Vue實(shí)例或是根data對(duì)象(ob.vmCount > 0),則無(wú)法新增數(shù)據(jù),直接返回。

接著處理能為target添加屬性的情況。不存在ob時(shí),說(shuō)明不是響應(yīng)式數(shù)據(jù),直接更新target。否則,執(zhí)行defineReactive函數(shù)為ob.value新增響應(yīng)式屬性,ob.value實(shí)際指向target,添加之后調(diào)用ob.dep.notify()通知觀察者重新求值,ob是Observer實(shí)例。

總結(jié)一下,set的內(nèi)部邏輯:

當(dāng)target是數(shù)組時(shí),更新長(zhǎng)度,調(diào)用變異方法splice插入新元素即可。

當(dāng)target是對(duì)象時(shí):

key在除Object.prototype外的原型鏈上時(shí),直接賦值

key在原型鏈上搜索不到時(shí),需要新增屬性。如果target無(wú)__ob__屬性,說(shuō)明不是響應(yīng)式數(shù)據(jù),直接賦值。否則調(diào)用defineReactive(ob.value, key, val)觀察新數(shù)據(jù),同時(shí)觸發(fā)依賴。

Vue.delete
刪除對(duì)象的屬性。如果對(duì)象是響應(yīng)式的,確保刪除能觸發(fā)更新視圖。

Vue.delete實(shí)際指向deldel接受兩個(gè)參數(shù),參數(shù)一target表示要?jiǎng)h除屬性的對(duì)象,參數(shù)二key表示要?jiǎng)h除的屬性名。

如果target是數(shù)組且key對(duì)于的索引在target中存在,使用變異方法splice方法直接刪除。

如果target是Vue實(shí)例或是根data對(duì)象則返回,不允許在其上刪除屬性。key不是實(shí)例自身屬性時(shí)也返回,不允許刪除。如果是自身屬性則使用delete刪除,接著判斷是否有__ob__屬性,如果有,說(shuō)明是響應(yīng)式數(shù)據(jù),執(zhí)行__ob__.dep.notify通知視圖更新。

export function del (target: Array | Object, key: any) {
  if (process.env.NODE_ENV !== "production" &&
    !Array.isArray(target) &&
    !isObject(target)
  ) {
    warn(`Cannot delete reactive property on non-object/array value: ${target}`)
  }
  // 數(shù)組 直接刪除元素
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.splice(key, 1)
    return
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== "production" && warn(
      "Avoid deleting properties on a Vue instance or its root $data " +
      "- just set it to null."
    )
    return
  }
  // 屬性不在target上
  if (!hasOwn(target, key)) {
    return
  }
  delete target[key]
  // 不是響應(yīng)式數(shù)據(jù)
  if (!ob) {
    return
  }
  ob.dep.notify()
}
小結(jié)

關(guān)于Observer類和set/get的源碼已經(jīng)做了簡(jiǎn)單的分析,細(xì)心的讀者可能會(huì)有一個(gè)問(wèn)題:target.__ob__.dep是什么時(shí)候收集依賴的。答案就在defineReactive的源碼中,其收集操作同樣在響應(yīng)式數(shù)據(jù)的getter中執(zhí)行。

至于defineReactive的源碼解析,在后面的文章再做分析。

參考鏈接

Vue技術(shù)內(nèi)幕|揭開(kāi)數(shù)據(jù)響應(yīng)系統(tǒng)的面紗

Vue源碼

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99435.html

相關(guān)文章

  • 淺析Vue響應(yīng)原理(三)

    摘要:響應(yīng)式原理之不論如何,最終響應(yīng)式數(shù)據(jù)都要通過(guò)來(lái)實(shí)現(xiàn),實(shí)際要借助新增的。在函數(shù)內(nèi),首先實(shí)例化一個(gè)實(shí)例,會(huì)在稍后添加為響應(yīng)式數(shù)據(jù)自定義的中發(fā)揮作用。只有數(shù)組和對(duì)象才可能是響應(yīng)式,才能返回實(shí)例。參考鏈接技術(shù)內(nèi)幕揭開(kāi)數(shù)據(jù)響應(yīng)系統(tǒng)的面紗源碼 Vue響應(yīng)式原理之defineReactive defineReactive 不論如何,最終響應(yīng)式數(shù)據(jù)都要通過(guò)defineReactive來(lái)實(shí)現(xiàn),實(shí)際要借助...

    tomener 評(píng)論0 收藏0
  • 從數(shù)組入手淺析Vue響應(yīng)原理

    摘要:響應(yīng)式原理為了探究這一切的原因,我再次點(diǎn)開(kāi)了的官網(wǎng)。在官網(wǎng)很下面的位置,找到了關(guān)于響應(yīng)式原理的說(shuō)明。因此,新添加到數(shù)組中的對(duì)象中的屬性,就成了非響應(yīng)式的屬性了,改變它自然不會(huì)讓組件重新渲染。響應(yīng)式屬性的對(duì)象,有這個(gè)對(duì)象就代表是響應(yīng)式的。 ??最近在用Vue開(kāi)發(fā)一個(gè)后臺(tái)管理的demo,有一個(gè)非常常規(guī)的需求。然而這個(gè)常規(guī)的需求中,包含了大量的知識(shí)點(diǎn)。有一個(gè)產(chǎn)品表格,用來(lái)顯示不同產(chǎn)品的信息。...

    dkzwm 評(píng)論0 收藏0
  • 淺析Vue響應(yīng)原理(一)

    摘要:淺析響應(yīng)式原理一的特點(diǎn)之一是響應(yīng)式,視圖隨著數(shù)據(jù)的更新而更新,在視圖中修改數(shù)據(jù)后實(shí)例中的數(shù)據(jù)也會(huì)同步更新。對(duì)于每個(gè)響應(yīng)式數(shù)據(jù),會(huì)有兩個(gè)實(shí)例,第一個(gè)是在中的閉包遍歷,用途顯而易見(jiàn)。接收一個(gè)回調(diào)函數(shù),會(huì)在重新求值且值更新后執(zhí)行。 淺析Vue響應(yīng)式原理(一) Vue的特點(diǎn)之一是響應(yīng)式,視圖隨著數(shù)據(jù)的更新而更新,在視圖中修改數(shù)據(jù)后Vue實(shí)例中的數(shù)據(jù)也會(huì)同步更新。內(nèi)部借助依賴(下文中的Dep類)...

    lookSomeone 評(píng)論0 收藏0
  • 前方來(lái)報(bào),八月最新資訊--關(guān)于vue2&3的最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰(shuí)只有你自己說(shuō)了才算,這是爹教我的道理。哪吒去他個(gè)鳥(niǎo)命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰(shuí)和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評(píng)論0 收藏0
  • 淺析RWD

    摘要:三響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)的基本原理標(biāo)簽,允許頁(yè)面寬度自動(dòng)調(diào)整大多數(shù)移動(dòng)瀏覽器將頁(yè)面放大為寬的視圖以符合屏幕分辨率。解決方案使用,選擇器清除浮動(dòng),只適用于非瀏覽器。由于移動(dòng)設(shè)備屏幕大小的限制,在其上進(jìn)行顯示的內(nèi)容一般是經(jīng)過(guò)精心篩選的。 一、前言 現(xiàn)今,無(wú)論是移動(dòng)設(shè)備、平板電腦、PC,屏幕大小各不相同,若是針對(duì)每個(gè)屏幕大小單獨(dú)設(shè)計(jì)一個(gè)解決方案,則會(huì)大幅增加網(wǎng)站建設(shè)的復(fù)雜程度和運(yùn)營(yíng)成本。響應(yīng)式網(wǎng)頁(yè)設(shè)...

    0x584a 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<