亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

Thanatos / 2164人閱讀

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

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

vue中的響應(yīng)式數(shù)據(jù)綁定是通過(guò)數(shù)據(jù)劫持和觀察者模式來(lái)實(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í)例化一個(gè)vue應(yīng)用的時(shí)候,會(huì)伴隨著各種的初始化工作,相關(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)
}

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

// src/core/instance/state.js

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

可以看到這里有對(duì)各種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方法是否返回的是一個(gè)對(duì)象,以及props中是否有與data中重名的屬性,最后會(huì)調(diào)用observe對(duì)data進(jìn)行監(jiān)聽(tīng),看一下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ò)展對(duì)象的話,就為它新建一個(gè)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ù)組就對(duì)數(shù)組元素再去調(diào)用observe方法做同樣的處理,如果不是,就調(diào)用walk去劫持該數(shù)據(jù),對(duì)數(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中的每一個(gè)成員都有一個(gè)對(duì)應(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è)面上對(duì)應(yīng)狀態(tài)被首次渲染的時(shí)候,會(huì)為頁(yè)面上每一個(gè)使用到data的地方新建一個(gè)watcher,并將當(dāng)前watcher保存到全局變量Dep.target中,在對(duì)應(yīng)data的getter中就會(huì)調(diào)用Dep.depend方法,將當(dāng)前的watcher添加到當(dāng)前的Dep中,一個(gè)Dep對(duì)應(yīng)一個(gè)或多個(gè)watcher,著取決于,此狀態(tài)被使用的數(shù)量。當(dāng)data被修改時(shí),對(duì)應(yīng)的setter就會(huì)被觸發(fā),會(huì)調(diào)用對(duì)應(yīng)的Dep中的notify方法,通知所有觀察者,進(jìn)行更新。

這里出現(xiàn)了兩個(gè)定的類: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和觀察者模式。

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

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

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

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

在頁(yè)面渲染的時(shí)候,頁(yè)面上每一個(gè)用到data的地方都會(huì)生成一個(gè)watcher,并將它保存到全局變量Dep.target中,watcher改變每一個(gè)觀察者,Dep用來(lái)管理觀察者。

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

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

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

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

相關(guān)文章

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

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

    malakashi 評(píng)論0 收藏0
  • Vue原理】VModel - 白話版

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

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

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

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

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

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

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

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

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

0條評(píng)論

Thanatos

|高級(jí)講師

TA的文章

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