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

資訊專欄INFORMATION COLUMN

vue中的數(shù)據(jù)綁定原理

Thanatos / 2126人閱讀

摘要:的響應(yīng)式數(shù)據(jù)綁定原文地址關(guān)鍵詞觀察者模式中的響應(yīng)式數(shù)據(jù)綁定是通過數(shù)據(jù)劫持和觀察者模式來實(shí)現(xiàn)的。

vue的響應(yīng)式數(shù)據(jù)綁定
原文地址:https://github.com/HolyZheng/...
關(guān)鍵詞:Object.defineProperty、觀察者模式

vue中的響應(yīng)式數(shù)據(jù)綁定是通過數(shù)據(jù)劫持和觀察者模式來實(shí)現(xiàn)的。當(dāng)前學(xué)習(xí)源碼為vue2.0
源碼關(guān)鍵目錄

src
|---core
|    |---instance
|          |---init.js
|          |---state.js
|    |---observer
|          |---dep.js
|          |---watcher.js

當(dāng)我們實(shí)例化一個vue應(yīng)用的時候,會伴隨著各種的初始化工作,相關(guān)的初始化工作代碼在init.js文件中

// src/core/instance/init.js

Vue.prototype._init = function (options?: Object) {
  ...
  initLifecycle(vm)
  initEvents(vm)
  callHook(vm, "beforeCreate")
  initState(vm)
  callHook(vm, "created")
  initRender(vm)
}

在這里可以看到對state的初始化工作initState()

// src/core/instance/state.js

export function initState (vm: Component) {
  vm._watchers = []
  initProps(vm)
  initData(vm)
  initComputed(vm)
  initMethods(vm)
  initWatch(vm)
}

可以看到這里有對各種sate的初始化工作,我們看initData()

// src/core/instance/state.js

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === "function"
    ? data.call(vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== "production" && warn(
      "data functions should return an object.",
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  let i = keys.length
  while (i--) {
    if (props && hasOwn(props, keys[i])) {
      process.env.NODE_ENV !== "production" && warn(
        `The data property "${keys[i]}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else {
      proxy(vm, keys[i])
    }
  }
  // observe data
  observe(data)
  data.__ob__ && data.__ob__.vmCount++
}

這里做了一點(diǎn)判斷,判斷data方法是否返回的是一個對象,以及props中是否有與data中重名的屬性,最后會調(diào)用observe對data進(jìn)行監(jiān)聽,看一下observe

// src/core/observer/index.js

export function observe (value: any): Observer | void {
  if (!isObject(value)) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    observerState.shouldConvert &&
    !config._isServer &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  return ob
}

可已看到這里也是做了一點(diǎn)判斷,如果有__ob__屬性的話就用它,或者如果data是數(shù)組或?qū)ο蠡蚩蓴U(kuò)展對象的話,就為它新建一個Observer,看一下Observer

// src/core/observer/index.js

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 through each property and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i], obj[keys[i]])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

判斷data是不是數(shù)組,如果是數(shù)組就對數(shù)組元素再去調(diào)用observe方法做同樣的處理,如果不是,就調(diào)用walk去劫持該數(shù)據(jù),對數(shù)據(jù)的劫持主要再defineReactive方法中,正如函數(shù)名,讓數(shù)據(jù)變得響應(yīng)式??匆幌耫efineReactive方法

// src/core/observer/index.js

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: Function
) {
  const dep = new Dep()
// data中的每一個成員都有一個對應(yīng)的Dep,在此閉包創(chuàng)建。

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set

  let childOb = observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend() // 依賴收集
        if (childOb) {
          childOb.dep.depend()
        }
        if (Array.isArray(value)) {
          for (let e, i = 0, l = value.length; i < l; i++) {
            e = value[i]
            e && e.__ob__ && e.__ob__.dep.depend()
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      if (newVal === value) {
        return
      }
      if (process.env.NODE_ENV !== "production" && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = observe(newVal)
      dep.notify() // 發(fā)布通知
    }
  })
}

遍歷狀態(tài),修改狀態(tài)的getter和setter,當(dāng)頁面上對應(yīng)狀態(tài)被首次渲染的時候,會為頁面上每一個使用到data的地方新建一個watcher,并將當(dāng)前watcher保存到全局變量Dep.target中,在對應(yīng)data的getter中就會調(diào)用Dep.depend方法,將當(dāng)前的watcher添加到當(dāng)前的Dep中,一個Dep對應(yīng)一個或多個watcher,著取決于,此狀態(tài)被使用的數(shù)量。當(dāng)data被修改時,對應(yīng)的setter就會被觸發(fā),會調(diào)用對應(yīng)的Dep中的notify方法,通知所有觀察者,進(jìn)行更新。

這里出現(xiàn)了兩個定的類:Dep和Watcher,其中Dep管理觀察者,Wathcer代表觀察者

先看一下Dep

// src/core/observer/dep.js

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
// 調(diào)用當(dāng)前target,也就是正在處理的watcher的addDep方法,并把此Dep傳進(jìn)去
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stablize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

看一下watcher.js

// src/core/observer/watcher.js

export default class Watcher {
...
  addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
       // 將當(dāng)前watcher添加到當(dāng)前的Dep中
        dep.addSub(this)
      }
    }
  }
...
}
總結(jié)

vue的響應(yīng)式數(shù)據(jù)綁定主要依賴Object.defineProperty和觀察者模式。

在我們新建一個vue實(shí)例的時候,做一系列的初始化工作,這部分的邏輯集中在src文件夾下的core文件夾下的instanceobserver文件夾內(nèi)

響應(yīng)式數(shù)據(jù)綁定是在狀態(tài)的初始化階段完成的,在initState方法中的initData中進(jìn)行data的數(shù)據(jù)綁定。

在initData中調(diào)用observe方法,為該data新建一個Observer類,然后最終調(diào)用為data中的每一個成員調(diào)用walk方法,在walk中通過defineReactive方法劫持當(dāng)前數(shù)據(jù)

在defineReactive中通過Object.defineProperty去修改數(shù)據(jù)的getter和setter

在頁面渲染的時候,頁面上每一個用到data的地方都會生成一個watcher,并將它保存到全局變量Dep.target中,watcher改變每一個觀察者,Dep用來管理觀察者。

然后在data的getter中將調(diào)用Dep的depend方法,將Dep.target中的watcher添加到此data對應(yīng)的Dep中,完成依賴收集

在data被修改的時候,對應(yīng)data的setter方法就會被出動,會調(diào)用Dep.notify()方法發(fā)布通知,調(diào)用每個watcher的uptade方法進(jìn)行更新。

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

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

相關(guān)文章

  • Vue面試題精選:Vue原理以及雙向數(shù)據(jù)綁定的實(shí)戰(zhàn)過程

    摘要:雙向數(shù)據(jù)綁定指的是,將對象屬性變化與視圖的變化相互綁定。數(shù)據(jù)雙向綁定已經(jīng)了解到是通過數(shù)據(jù)劫持的方式來做數(shù)據(jù)綁定的,其中最核心的方法便是通過來實(shí)現(xiàn)對屬性的劫持,達(dá)到監(jiān)聽數(shù)據(jù)變動的目的。和允許觀察數(shù)據(jù)的更改并觸發(fā)更新。 1 MVVM 雙向數(shù)據(jù)綁定指的是,將對象屬性變化與視圖的變化相互綁定。換句話說,如果有一個擁有name屬性的user對象,與元素的內(nèi)容綁定,當(dāng)給user.name賦予一個新...

    malakashi 評論0 收藏0
  • Vue原理】VModel - 白話版

    摘要:執(zhí)行的時候,會綁定上下文對象為組件實(shí)例于是中的就能取到組件實(shí)例本身,的代碼塊頂層作用域就綁定為了組件實(shí)例于是內(nèi)部變量的訪問,就會首先訪問到組件實(shí)例上。其中的獲取,就會先從組件實(shí)例上獲取,相當(dāng)于。 寫文章不容易,點(diǎn)個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得...

    keke 評論0 收藏0
  • Vue簡單實(shí)現(xiàn)原理

    摘要:需要指令解析器,對每個元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)。然后將模版中的變量替換成數(shù)據(jù),渲染,將每個指令對應(yīng)的節(jié)點(diǎn)綁定更新函數(shù),添加監(jiān)聽數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)發(fā)生變動,收到通知,更新視圖。 用了Vue也有兩年時間了,一直以來都是只知其然,不知其所以然,為了能更好的使用Vue不被Vue所奴役,學(xué)習(xí)一下Vue底層的基本原理。 Vue官網(wǎng)有一段這樣的介...

    luqiuwen 評論0 收藏0
  • vue框架的基本原理,簡單實(shí)現(xiàn)一個todo-list

    摘要:前言最近在學(xué)習(xí)框架的基本原理,看了一些技術(shù)博客以及一些對源碼的簡單實(shí)現(xiàn),對數(shù)據(jù)代理數(shù)據(jù)劫持模板解析變異數(shù)組方法雙向綁定有了更深的理解。 前言 最近在學(xué)習(xí)vue框架的基本原理,看了一些技術(shù)博客以及一些對vue源碼的簡單實(shí)現(xiàn),對數(shù)據(jù)代理、數(shù)據(jù)劫持、模板解析、變異數(shù)組方法、雙向綁定有了更深的理解。于是乎,嘗試著去實(shí)踐自己學(xué)到的知識,用vue的一些基本原理實(shí)現(xiàn)一個簡單的todo-list,完成...

    Karrdy 評論0 收藏0
  • 太原面經(jīng)分享:如何在vue面試環(huán)節(jié),展示你晉級阿里P6+的技術(shù)功底?

    摘要:假如你通過閱讀源碼,掌握了對的實(shí)現(xiàn)原理,對生態(tài)系統(tǒng)有了充分的認(rèn)識,那你會在面試環(huán)節(jié)游刃有余,達(dá)到晉級阿里的技術(shù)功底,從而提高個人競爭力,面試加分更容易拿。 前言 一年一度緊張刺激的高考開始了,與此同時,我也沒閑著,奔走在各大公司的前端面試環(huán)節(jié),不斷積累著經(jīng)驗,一路升級打怪。 最近兩年,太原作為一個準(zhǔn)二線城市,各大互聯(lián)網(wǎng)公司的技術(shù)棧也在升級換代,假如你在太原面試前端崗位,而你的技術(shù)庫里若...

    xiaoqibTn 評論0 收藏0

發(fā)表評論

0條評論

Thanatos

|高級講師

TA的文章

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