欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2016年12月21日 13:57:15   作者:史俊男  
這篇文章主要教大家如何簡(jiǎn)單實(shí)現(xiàn)Vue的observer和watcher,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

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

在日常項(xiàng)目開發(fā)的時(shí)候,我們將js對(duì)象傳給vue實(shí)例中的data選項(xiàng),來作為其更新視圖的基礎(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)什么
 }
 })

接下來可以利用其實(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)聽啦')})
vm.$data.va = 1

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

咦,說好的遞歸呢,不著急,上面只是抽離了加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自身~我們改寫下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),一直到最后一層,從最后一層開始執(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)來的值為復(fù)雜類型,再遞歸它,加上set/get
  childObj = observe(val)
 }
 })
}

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

var vm = { _data: {}, $data: {}, callback: {}}
var defineReactive = (obj, key) => {
 // 一開始的時(shí)候是不設(shè)值的,所以,要在外面做一套o(hù)bserve
 // 判斷val是否為對(duì)象,如果對(duì)象有很多層屬性,則這邊的代碼會(huì)不斷調(diào)用自身(因?yàn)閛bserve又執(zhí)行了Observer,而Observer執(zhí)行defineReactive),一直到最后一層,從最后一層開始執(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)來的值為復(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)聽啦')})
vm.$data.va = 1

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

什么鬼?

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

var vm = { _data: {}, $data: {}, callback: {}}
var defineReactive = (obj, key, cb) => {
 // 一開始的時(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)來的值為復(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)聽啦')})
vm.$data.va.a = 1

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

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

下一篇將會(huì)結(jié)合源碼來淺談下vue的watcher和observer

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解vue.js之綁定class和style的示例代碼

    詳解vue.js之綁定class和style的示例代碼

    本篇文章主要介紹了詳解vue.js之綁定class和style的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • 淺談vue的生命周期

    淺談vue的生命周期

    這篇文章主要為大家介紹了vue的生命周期,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Vue如何通過瀏覽器控制臺(tái)查看全局data值

    Vue如何通過瀏覽器控制臺(tái)查看全局data值

    在寫vue項(xiàng)目時(shí)想到一個(gè)問題,項(xiàng)目里面的文件都是一個(gè)個(gè)的組件,如何在控制臺(tái)中修改,查看組件data里的值呢,下面這篇文章主要給大家介紹了關(guān)于Vue如何通過瀏覽器控制臺(tái)查看全局data值的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • vue配置啟動(dòng)項(xiàng)目自動(dòng)打開瀏覽器方式

    vue配置啟動(dòng)項(xiàng)目自動(dòng)打開瀏覽器方式

    這篇文章主要介紹了vue配置啟動(dòng)項(xiàng)目自動(dòng)打開瀏覽器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue3生命周期函數(shù)和方法詳解

    Vue3生命周期函數(shù)和方法詳解

    本文詳細(xì)講解了Vue3生命周期函數(shù)和方法。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 使用vue-router切換組件時(shí)使組件不銷毀問題

    使用vue-router切換組件時(shí)使組件不銷毀問題

    這篇文章主要介紹了使用vue-router切換組件時(shí)使組件不銷毀問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Vue權(quán)限指令控制權(quán)限詳解

    Vue權(quán)限指令控制權(quán)限詳解

    因?yàn)轫?xiàng)目中需要根據(jù)后端返回的權(quán)限進(jìn)行功能的顯示隱藏,所以就加了個(gè)權(quán)限指令。不用寫if else進(jìn)行判斷,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • vue3點(diǎn)擊不同的菜單頁(yè)切換局部頁(yè)面實(shí)現(xiàn)方法

    vue3點(diǎn)擊不同的菜單頁(yè)切換局部頁(yè)面實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于vue3點(diǎn)擊不同的菜單頁(yè)切換局部頁(yè)面實(shí)現(xiàn)的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue3具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-08-08
  • Vue在項(xiàng)目中如何引入本地Json數(shù)據(jù)

    Vue在項(xiàng)目中如何引入本地Json數(shù)據(jù)

    這篇文章主要介紹了Vue在項(xiàng)目中如何引入本地Json數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue打包通過image-webpack-loader插件對(duì)圖片壓縮優(yōu)化操作

    vue打包通過image-webpack-loader插件對(duì)圖片壓縮優(yōu)化操作

    這篇文章主要介紹了vue打包通過image-webpack-loader插件對(duì)圖片壓縮優(yōu)化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11

最新評(píng)論