国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

淺析Vue響應式原理(二)

rockswang / 2229人閱讀

摘要:響應式原理之之前簡單介紹了和類的代碼和作用,現在來介紹一下類和。對于數組,響應式的實現稍有不同。不存在時,說明不是響應式數據,直接更新。如果對象是響應式的,確保刪除能觸發更新視圖。

Vue響應式原理之Observer

之前簡單介紹了Dep和Watcher類的代碼和作用,現在來介紹一下Observer類和set/get。在Vue實例后再添加響應式數據時需要借助Vue.set/vm.$set方法,這兩個方法內部實際上調用了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有三個屬性。value是響應式數據的值;dep是Dep實例,這個Dep實例用于Vue.set/vm.$set中通知依賴更新;vmCount表示把這個數據當成根data對象的實例數量,大于0時是實例化傳入的根data對象。

構造函數接受一個值,表示要觀察的值,這樣,在Observer實例中引用了響應式數據,并將響應式數據的__ob__屬性指向自身。如果被觀察值是除數組以外的類型,會調用walk方法,令每個屬性都是響應式。對于基本類型的值,Object.keys會返回一個空數組,所以在walk內,defineReactive只在對象的屬性上執行。如果是被觀察值是數組,那么會在每個元素上調用工廠函數observe,使其響應式。

對于數組,響應式的實現稍有不同。回顧一下在教程數組更新檢測里的說明,變異方法會觸發視圖更新。其具體實現就在這里。arrayMethods是一個對象,保存了Vue重寫的數組方法,具體重寫方式下面再說,現在只需知道這些重寫的數組方法除了保持原數組方法的功能外,還能通知依賴數據已更新。augment的用途是令value能夠調用在arrayMethods中的方法,實現的方式有兩種。第一種是通過原型鏈實現,在value.__proto__添加這些方法,優先選擇這種實現。部分瀏覽器不支持__proto__,則直接在value上添加這些方法。

最后執行observeArray方法,遍歷value,在每個元素上執行observe方法。

數組變異方法的實現

執行變異方法會觸發視圖功能,所以變異方法要實現的功能,除了包括原來數組方法的功能外,還要有通知依賴數據更新的功能。代碼保存在/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
  })
})

模塊內,使用arrayProto保存數組原型,arrayMethods的原型是arrayProto,用來保存變異后的方法,methodsToPatch是保存變異方法名的數組。

遍歷methodsToPatch,根據方法名來獲取在arrayProto上的數組變異方法,然后在arrayMethods實現同名方法。

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

observe

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

isObject判斷value是不是Object類型,實現如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實例ob之間是雙向引用。value.__ob__指向ob,ob.value指向value

Vue.set

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

Vue.set/vm.$set內部都是在/src/code/observer/index.js定義的set的函數。

set函數接受三個參數,參數一target表示要新增屬性的對象,參數二key表示新增的屬性名或索引,參數三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 說明不是響應式數據
  if (!ob) {
    target[key] = val
    return val
  }
  // 為target添加新屬性
  defineReactive(ob.value, key, val)
  // ob.dep實際是target.__ob__.dep
  ob.dep.notify()
  return val
}

函數內部首先判斷target類型,非數組或非對象的目標數據是無法添加響應式數據的。

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

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

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

總結一下,set的內部邏輯:

target是數組時,更新長度,調用變異方法splice插入新元素即可。

target是對象時:

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

key在原型鏈上搜索不到時,需要新增屬性。如果target__ob__屬性,說明不是響應式數據,直接賦值。否則調用defineReactive(ob.value, key, val)觀察新數據,同時觸發依賴。

Vue.delete
刪除對象的屬性。如果對象是響應式的,確保刪除能觸發更新視圖。

Vue.delete實際指向deldel接受兩個參數,參數一target表示要刪除屬性的對象,參數二key表示要刪除的屬性名。

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

如果target是Vue實例或是根data對象則返回,不允許在其上刪除屬性。key不是實例自身屬性時也返回,不允許刪除。如果是自身屬性則使用delete刪除,接著判斷是否有__ob__屬性,如果有,說明是響應式數據,執行__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}`)
  }
  // 數組 直接刪除元素
  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]
  // 不是響應式數據
  if (!ob) {
    return
  }
  ob.dep.notify()
}
小結

關于Observer類和set/get的源碼已經做了簡單的分析,細心的讀者可能會有一個問題:target.__ob__.dep是什么時候收集依賴的。答案就在defineReactive的源碼中,其收集操作同樣在響應式數據的getter中執行。

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

參考鏈接

Vue技術內幕|揭開數據響應系統的面紗

Vue源碼

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99435.html

相關文章

  • 淺析Vue響應原理(三)

    摘要:響應式原理之不論如何,最終響應式數據都要通過來實現,實際要借助新增的。在函數內,首先實例化一個實例,會在稍后添加為響應式數據自定義的中發揮作用。只有數組和對象才可能是響應式,才能返回實例。參考鏈接技術內幕揭開數據響應系統的面紗源碼 Vue響應式原理之defineReactive defineReactive 不論如何,最終響應式數據都要通過defineReactive來實現,實際要借助...

    tomener 評論0 收藏0
  • 從數組入手淺析Vue響應原理

    摘要:響應式原理為了探究這一切的原因,我再次點開了的官網。在官網很下面的位置,找到了關于響應式原理的說明。因此,新添加到數組中的對象中的屬性,就成了非響應式的屬性了,改變它自然不會讓組件重新渲染。響應式屬性的對象,有這個對象就代表是響應式的。 ??最近在用Vue開發一個后臺管理的demo,有一個非常常規的需求。然而這個常規的需求中,包含了大量的知識點。有一個產品表格,用來顯示不同產品的信息。...

    dkzwm 評論0 收藏0
  • 淺析Vue響應原理(一)

    摘要:淺析響應式原理一的特點之一是響應式,視圖隨著數據的更新而更新,在視圖中修改數據后實例中的數據也會同步更新。對于每個響應式數據,會有兩個實例,第一個是在中的閉包遍歷,用途顯而易見。接收一個回調函數,會在重新求值且值更新后執行。 淺析Vue響應式原理(一) Vue的特點之一是響應式,視圖隨著數據的更新而更新,在視圖中修改數據后Vue實例中的數據也會同步更新。內部借助依賴(下文中的Dep類)...

    lookSomeone 評論0 收藏0
  • 前方來報,八月最新資訊--關于vue2&3的最佳文章推薦

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

    izhuhaodev 評論0 收藏0
  • 淺析RWD

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

    0x584a 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<