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

資訊專欄INFORMATION COLUMN

簡(jiǎn)單實(shí)現(xiàn)Vue的observer和watcher

Thanatos / 2418人閱讀

非庖丁瞎解牛系列~ =。=

在日常項(xiàng)目開(kāi)發(fā)的時(shí)候,我們將js對(duì)象傳給vue實(shí)例中的data選項(xiàng),來(lái)作為其更新視圖的基礎(chǔ),事實(shí)上是vue將會(huì)遍歷它的屬性,用Object.defineProperty 設(shè)置它們的 get/set,從而讓 data 的屬性能夠響應(yīng)數(shù)據(jù)變化:

 Object.defineProperty(obj, name, {
   // 獲取值的時(shí)候先置入vm的_data屬性對(duì)象中
   get() {
     // 賦值的時(shí)候顯示的特性
   },
   set() {
     // 值變化的時(shí)候可以做點(diǎn)什么
   }
 })

接下來(lái)可以利用其實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的watcher.既然要綁定數(shù)據(jù)執(zhí)行回調(diào)函數(shù),data屬性和callback屬性是少不了的,我們定義一個(gè)vm對(duì)象(vue中vm對(duì)象作為根實(shí)例,是全局的):

/**
 * @param {Object} _data 用于存放data值
 * @param {Object} $data data原始數(shù)據(jù)對(duì)象,當(dāng)前值
 * @param {Object} callback 回調(diào)函數(shù)
 */
var vm = { _data: {}, $data: {}, callback: {} }

在設(shè)置值的時(shí)候,如果檢測(cè)到當(dāng)前值與存儲(chǔ)在_data中的對(duì)應(yīng)值發(fā)生變化,則將值更新,并執(zhí)行回調(diào)函數(shù),利用Object.definedProperty方法中的get() & set() 我們很快就可以實(shí)現(xiàn)這個(gè)功能~

 vm.$watch = (obj, func) => {
    // 回調(diào)函數(shù)
    vm.callback[ obj ] = func
    // 設(shè)置data
    Object.defineProperty(vm.$data, obj, {
      // 獲取值的時(shí)候先置入vm的_data屬性對(duì)象中
      get() {
        return vm._data[ obj ]
      },
      set(val) {
        // 比較原值,不相等則賦值,執(zhí)行回調(diào)
        if (val !== vm._data[ obj ]) {
          vm._data[ obj ] = val
          const cb = vm.callback[ obj ]
          cb.call(vm)
        }
      }
   })
}
vm.$watch("va", () => {console.log("已經(jīng)成功被監(jiān)聽(tīng)啦")})
vm.$data.va = 1

雖然初步實(shí)現(xiàn)了這個(gè)小功能,那么問(wèn)題來(lái)了,obj對(duì)象如果只是一個(gè)簡(jiǎn)單的值為值類型的變量,那以上代碼完全可以滿足;但是如果obj是一個(gè)具有一層甚至多層樹(shù)結(jié)構(gòu)對(duì)象變量,我們就只能監(jiān)聽(tīng)到最外層也就是obj本身的變化,內(nèi)部屬性變化無(wú)法被監(jiān)聽(tīng)(沒(méi)有設(shè)置給對(duì)應(yīng)屬性設(shè)置set和get),因?yàn)閷?duì)象自身內(nèi)部屬性層數(shù)未知,理論上可以無(wú)限層(一般不會(huì)這么做),所以此處還是用遞歸解決吧~

咱們先將Object.defineProperty函數(shù)剝離,一是解耦,二是方便我們遞歸~

var defineReactive = (obj, key) => {
  Object.defineProperty(obj, key, {
    get() {
      return vm._data[key]
    },
    set(newVal) {
      if (vm._data[key] === newVal) {
        return
      }
      vm._data[key] = newVal
      const cb = vm.callback[ obj ]
      cb.call(vm)
    }
  })
}

咦,說(shuō)好的遞歸呢,不著急,上面只是抽離了加get和set功能的函數(shù),
現(xiàn)在我們加入遞歸~

var Observer = (obj) => {
  // 遍歷,讓對(duì)象中的每個(gè)屬性可以加上get set
  Object.keys(obj).forEach((key) =>{
    defineReactive(obj, key)
  })
}

這里僅僅只是遍歷,要達(dá)到遞歸,則需要在defineReactive的時(shí)候再加上判斷,判斷這個(gè)屬性是否為object類型,如果是,則執(zhí)行Observer自身~我們改寫(xiě)下defineReactive函數(shù)

// 判斷是否為object類型,是就繼續(xù)執(zhí)行自身
var observe = (value) => {
  // 判斷是否為object類型,是就繼續(xù)執(zhí)行Observer
  if (!value || typeof value !== "object") {
    return
  }
  return new Observer(value)
}

// 將observe方法置入defineReactive中Object.defineProperty的set中,形成遞歸
var defineReactive = (obj, key) => {
  // 判斷val是否為對(duì)象,如果對(duì)象有很多層屬性,則這邊的代碼會(huì)不斷調(diào)用自身(因?yàn)閛bserve又執(zhí)行了Observer,而Observer執(zhí)行defineReactive),一直到最后一層,從最后一層開(kāi)始執(zhí)行下列代碼,層層返回(可以理解為洋蔥模型),直到最前面一層,給所有屬性加上get/set
  var childObj = observe(vm._data[key])
  Object.defineProperty(obj, key, {
    get() {
      return vm._data[key]
    },
    set(newVal) {
      // 如果設(shè)置的值完全相等則什么也不做
      if (vm._data[key] === newVal) {
         return
      }
      // 不相等則賦值
      vm._data[key] = newVal
      // 執(zhí)行回調(diào)
      const cb = vm.callback[ key ]
      cb.call(vm)
      // 如果set進(jìn)來(lái)的值為復(fù)雜類型,再遞歸它,加上set/get
      childObj = observe(val)
    }
  })
}

現(xiàn)在我們來(lái)整理下,把我們剛開(kāi)始實(shí)現(xiàn)的功能雛形進(jìn)行進(jìn)化

var vm = { _data: {}, $data: {}, callback: {}}
var defineReactive = (obj, key) => {
  // 一開(kāi)始的時(shí)候是不設(shè)值的,所以,要在外面做一套o(hù)bserve
  // 判斷val是否為對(duì)象,如果對(duì)象有很多層屬性,則這邊的代碼會(huì)不斷調(diào)用自身(因?yàn)閛bserve又執(zhí)行了Observer,而Observer執(zhí)行defineReactive),一直到最后一層,從最后一層開(kāi)始執(zhí)行下列代碼,層層返回(可以理解為洋蔥模型),直到最前面一層,給所有屬性加上get/set
  var childObj = observe(vm._data[key])
  Object.defineProperty(obj, key, {
    get() {
      return vm._data[key]
    },
    set(newVal) {
      if (vm._data[key] === newVal) {
        return
      }
    // 如果值有變化的話,做一些操作
    vm._data[key] = newVal
    // 執(zhí)行回調(diào)
    const cb = vm.callback[ key ]
    cb.call(vm)
    // 如果set進(jìn)來(lái)的值為復(fù)雜類型,再遞歸它,加上set/get
    childObj = observe(newVal)
    }
  })
}
var Observer = (obj) => {
  Object.keys(obj).forEach((key) =>{
    defineReactive(obj, key)
  })
}
var observe = (value) => {
  // 判斷是否為object類型,是就繼續(xù)執(zhí)行Observer
  if (!value || typeof value !== "object") {
    return
  }
  Observer(value)
}
vm.$watch = (name, func) => {
  // 回調(diào)函數(shù)
  vm.callback[name] = func
  // 設(shè)置data
  defineReactive(vm.$data, name)
}
// 綁定a,a若變化則執(zhí)行回調(diào)方法
var va = {a:{c: "c"}, b:{c: "c"}}
vm._data[va] = {a:{c: "c"}, b:{c: "c"}}
vm.$watch("va", () => {console.log("已經(jīng)成功被監(jiān)聽(tīng)啦")})
vm.$data.va = 1

在谷歌瀏覽器的console中粘貼以上代碼,然后回車發(fā)現(xiàn),結(jié)果不出所料,va本身被監(jiān)聽(tīng)了,可以,我們?cè)囋噕a的內(nèi)部屬性有沒(méi)有被監(jiān)聽(tīng),改下vm.$data.va = 1為vm.$data.va.a = 1,結(jié)果發(fā)現(xiàn)報(bào)錯(cuò)了

什么鬼?

我們又仔細(xì)檢查了代碼,WTF,原來(lái)我們?cè)谶f歸的時(shí)候,Object.defineProperty中的回調(diào)函數(shù)cb的key參數(shù)一直在發(fā)生變化,我們希望的是里面的屬性變化的時(shí)候執(zhí)行的是我們事先定義好的回調(diào)函數(shù)~那么我們來(lái)改下方法,將一開(kāi)始定義好的回調(diào)作為參數(shù)傳進(jìn)去,確保每一層遞歸set的回調(diào)都是我們事先設(shè)置好的~

var vm = { _data: {}, $data: {}, callback: {}}
var defineReactive = (obj, key, cb) => {
  // 一開(kāi)始的時(shí)候是不設(shè)值的,所以,要在外面做一套o(hù)bserve
  var childObj = observe(vm._data[key], cb)
  Object.defineProperty(obj, key, {
    get() {
      return vm._data[key]
    },
    set(newVal) {
      if (vm._data[key] === newVal) {
        return
      }
      // 如果值有變化的話,做一些操作
      vm._data[key] = newVal
      // 執(zhí)行回調(diào)
      cb()
      // 如果set進(jìn)來(lái)的值為復(fù)雜類型,再遞歸它,加上set/get
      childObj = observe(newVal)
    }
  })
}
var Observer = (obj, cb) => {
  Object.keys(obj).forEach((key) =>{
    defineReactive(obj, key, cb)
  })
}
var observe = (value, cb) => {
  // 判斷是否為object類型,是就繼續(xù)執(zhí)行Observer
  if (!value || typeof value !== "object") {
    return
  }
  Observer(value, cb)
}
vm.$watch = (name, func) => {
  // 回調(diào)函數(shù)
  vm.callback[name] = func
  // 設(shè)置data
  defineReactive(vm.$data, name, func)
}
// 綁定a,a若變化則執(zhí)行回調(diào)方法
var va = {a:{c: "c"}, b:{c: "c"}}
vm._data.va = {a:{c: "c"}, b:{c: "c"}}
vm.$watch("va", () => {console.log("又成功被監(jiān)聽(tīng)啦")})
vm.$data.va.a = 1

再執(zhí)行一次以上代碼,發(fā)現(xiàn)內(nèi)部的a屬性也被監(jiān)聽(tīng)到了,而且屬性值變化的時(shí)候執(zhí)行了我們事先定義好的回調(diào)函數(shù)~嘻嘻嘻~

雖然實(shí)現(xiàn)了$watch的基本功能,但是和vue的源碼還是有一定的距離,特別是一些扁平化和模塊化的思想需要涉及到一些設(shè)計(jì)模式,其實(shí)我們?cè)诳丛创a的時(shí)候,常常是逆著作者的思維走的,功能從簡(jiǎn)單到復(fù)雜往往涉及到代碼的模塊化和解耦,使得代碼非常地分散,讀起來(lái)晦澀難懂,自己動(dòng)手,從小功能代碼塊實(shí)現(xiàn),然后結(jié)合源碼,對(duì)比思路,慢慢豐富,也不失為一種學(xué)習(xí)源碼的方式~

ps: 如果各位讀者看到本文的error或者由更好的優(yōu)化建議,隨時(shí)聯(lián)系~

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

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

相關(guān)文章

  • vue 源碼分析之如何實(shí)現(xiàn) observer watcher

    摘要:所以,我們是不是應(yīng)該寫(xiě)一個(gè)消息訂閱器呢這樣的話,一觸發(fā)方法,我們就發(fā)一個(gè)通知出來(lái),然后,訂閱這個(gè)消息的,就會(huì)怎樣。。。截止到現(xiàn)在,在我們只考慮最簡(jiǎn)單情況下。。關(guān)于的新文章行代碼,理解和分析的響應(yīng)式架構(gòu) 本文能幫你做什么?。。好奇vue雙向綁定的同學(xué),可以部分緩解好奇心還可以幫你了解如何實(shí)現(xiàn)$watch 前情回顧 我之前寫(xiě)了一篇沒(méi)什么干貨的文章。。并且刨了一個(gè)大坑。。今天。。打算來(lái)填一天...

    shiina 評(píng)論0 收藏0
  • Vue雙向綁定實(shí)現(xiàn)原理系列(三):監(jiān)聽(tīng)器Observer訂閱者Watcher

    摘要:至此監(jiān)聽(tīng)器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽(tīng)器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器 監(jiān)聽(tīng)器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過(guò)程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分 github源碼 1.數(shù)據(jù)監(jiān)聽(tīng)器Obser...

    widuu 評(píng)論0 收藏0
  • Vue雙向綁定實(shí)現(xiàn)原理系列(三):監(jiān)聽(tīng)器Observer訂閱者Watcher

    摘要:至此監(jiān)聽(tīng)器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽(tīng)器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器 監(jiān)聽(tīng)器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過(guò)程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分 github源碼 1.數(shù)據(jù)監(jiān)聽(tīng)器Obser...

    legendaryedu 評(píng)論0 收藏0
  • 深入淺出Vue響應(yīng)式原理

    摘要:總結(jié)最后我們依照下圖參考深入淺出,再來(lái)回顧下整個(gè)過(guò)程在后,會(huì)調(diào)用函數(shù)進(jìn)行初始化,也就是過(guò)程,在這個(gè)過(guò)程通過(guò)轉(zhuǎn)換成了的形式,來(lái)對(duì)數(shù)據(jù)追蹤變化,當(dāng)被設(shè)置的對(duì)象被讀取的時(shí)候會(huì)執(zhí)行函數(shù),而在當(dāng)被賦值的時(shí)候會(huì)執(zhí)行函數(shù)。 前言 Vue 最獨(dú)特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 JavaScript 對(duì)象。而當(dāng)你修改它們時(shí),視圖會(huì)進(jìn)行更新。這使得狀態(tài)管理非常簡(jiǎn)單直接,不過(guò)理解...

    yiliang 評(píng)論0 收藏0
  • Vue 數(shù)據(jù)響應(yīng)式原理

    摘要:接下來(lái),我們就一起深入了解的數(shù)據(jù)響應(yīng)式原理,搞清楚響應(yīng)式的實(shí)現(xiàn)機(jī)制?;卣{(diào)函數(shù)只是打印出新的得到的新的值,由執(zhí)行后生成。及異步更新相信讀過(guò)前文,你應(yīng)該對(duì)響應(yīng)式原理有基本的認(rèn)識(shí)。 前言 Vue.js 的核心包括一套響應(yīng)式系統(tǒng)。 響應(yīng)式,是指當(dāng)數(shù)據(jù)改變后,Vue 會(huì)通知到使用該數(shù)據(jù)的代碼。例如,視圖渲染中使用了數(shù)據(jù),數(shù)據(jù)改變后,視圖也會(huì)自動(dòng)更新。 舉個(gè)簡(jiǎn)單的例子,對(duì)于模板: {{ name ...

    Mike617 評(píng)論0 收藏0
  • vue.js動(dòng)態(tài)數(shù)據(jù)綁定學(xué)習(xí)

    摘要:對(duì)于的動(dòng)態(tài)數(shù)據(jù)綁定,經(jīng)過(guò)反復(fù)地看源碼和博客講解,總算能夠理解它的實(shí)現(xiàn)了,心累分享一下學(xué)習(xí)成果,同時(shí)也算是做個(gè)記錄。 對(duì)于vue.js的動(dòng)態(tài)數(shù)據(jù)綁定,經(jīng)過(guò)反復(fù)地看源碼和博客講解,總算能夠理解它的實(shí)現(xiàn)了,心累~ 分享一下學(xué)習(xí)成果,同時(shí)也算是做個(gè)記錄。完整代碼GitHub地址:https://github.com/hanrenguang/Dynamic-data-binding。也可以到倉(cāng)庫(kù)...

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

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

0條評(píng)論

Thanatos

|高級(jí)講師

TA的文章

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