vue實(shí)現(xiàn)雙向數(shù)據(jù)綁定
本文實(shí)例為大家分享了vue實(shí)現(xiàn)雙向數(shù)據(jù)綁定的具體代碼,供大家參考,具體內(nèi)容如下
vue中數(shù)組與對(duì)象采用了不同的綁定方式
1.vue對(duì)象數(shù)據(jù)綁定
(1)數(shù)據(jù)偵測(cè)
在js中,我們使用Object.defineProperty()和ES6的proxy來對(duì)對(duì)象進(jìn)行偵測(cè)
在vue2.x中使用的是Object.defineProperty()來對(duì)對(duì)象進(jìn)行數(shù)據(jù)偵測(cè),我們首先對(duì)Object.defineProperty進(jìn)行封裝,有如下的代碼:
function defineReactive(data, key, val){ if(typeof val === 'object') new Observer(val) let dep = new Dep(); Object.defineProperty(data, key, val) { enumerable: true, configurable: true, get: function () { dep.depend(); return val; }, set: function() { if(val === newVal) { return ; } val = newVal; dep.notify() } } }
需要傳遞的參數(shù)只有data, key, val,每當(dāng)從data中的key中讀取數(shù)據(jù)的時(shí)候觸發(fā)get,修改的數(shù)據(jù)的時(shí)候觸發(fā)set
(2)依賴收集
先收集依賴,等到屬性發(fā)生改變的時(shí)候,再把收集到的依賴循環(huán)觸發(fā)一遍就好了,在getter中收集依賴,在setter中觸發(fā)依賴
依賴類Dep
export default class Dep { constructor() { this.subs = [] } addSub() { this.subs.push(sub) } removeSub(sub) { remove(this.subs, sub) } depend() { if(window.target) { this.addSub(window.target) } } notify() { const subs = this.subs.slice() for(let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } function remove(arr, item) { if(arr.length) { const index = arr.indexOf(item) if(index > -1) { return arr.splice(index, 1) } } }
watcher類是我們收集的依賴
export default class Watcher { constructor(vm, expOrFn, cb) { this.vm = vm this.getter = parsePath(expOrFn) this.cb = cb this.value = this.get() } get() { window.target = this let value = this.getter.call(this.vm, this.vm) window.target = undefined return value } update() { const oldValue = this.value this.value = this.get() this.cb.call(this.vm, this.value, oldValue) } }
(3)遞歸偵測(cè)所有key (Observer)
export class Observer { constructor(value) { this.value = value; if(!Array.isArray(value)) { this.walk(value) } } walk(obj) { const keys = Object.keys(obj) for(let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } }
Observer類就是將對(duì)象的所有屬性變?yōu)轫憫?yīng)式的
2.Array的變化偵測(cè)
(1)數(shù)組中追蹤變化用的是攔截器覆蓋原型方法
const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto); //和數(shù)組原型一樣的對(duì)象作為攔截器 ['push','pop','shift','unshift','splice','sort','reverse'].forEach(function (method) { const original = arrayProto[method] Object.defineProperty(arrayMethods, method, { value: function mutator(...args) { return original.apply(this, args) }, enumerable: false, writable: true, configurable: true }) })
攔截器覆蓋原型只有一句話
value.__proto__ = arrayMethods
如果沒有__proto__屬性,vue將會(huì)把這些arrayMethods掛載到被偵測(cè)的數(shù)組上
數(shù)組與對(duì)象類似,都是在getter中收集依賴,而數(shù)組觸發(fā)依賴實(shí)在攔截器里面
數(shù)組的依賴保存在Observer實(shí)例上,依賴必須是getter和攔截器中都可以訪問到的
__ob__是不可枚舉屬性,這個(gè)屬性的值就是當(dāng)前Observer實(shí)例
把Dep實(shí)例保存在Observer的屬性上,如果value已經(jīng)有__ob__屬性,則不需要重復(fù)創(chuàng)建Observer實(shí)例,避免重復(fù)偵測(cè)value的變化
像數(shù)組的依賴發(fā)送通知
this.__ob__.dep.notify();
(2).偵測(cè)數(shù)據(jù)變化的具體方法
循環(huán)數(shù)組中的每一項(xiàng),執(zhí)行observe函數(shù)來偵測(cè)變化
observeArray(items) { for(let i = 0; l = items.length; i < l; i++) { observe(items[i]); } }
數(shù)組需要偵測(cè)新的元素
通過攔截push,unshift,splice等方法,并且把a(bǔ)rgs存儲(chǔ)在inserted中
if(inserted) ob.observeArray(inserted)
總結(jié):
Array追蹤變化的方式和Object不一樣,所以我們通過創(chuàng)建攔截器去覆蓋數(shù)組原型的方式來追蹤變化,為了不污染全局Array.prototype,我們?cè)贠bserver中只針對(duì)需要偵測(cè)變化的數(shù)組使用__proto__來覆蓋原型。
Array收集依賴的方式和Object一樣,都在getter中收集,在攔截器中觸發(fā),依賴保存在Observer實(shí)例上。在Observer上,我們每個(gè)偵測(cè)的數(shù)據(jù)都標(biāo)記上了__ob__,并把this(Observer)保存在__ob__上,主要是為了保證同一數(shù)據(jù)只被偵測(cè)一次,另外可以很方便的通過__ob__從而拿到Observer實(shí)例上保存的依賴。數(shù)組需要循環(huán)把每一個(gè)數(shù)組項(xiàng)都變成響應(yīng)式的,當(dāng)數(shù)組中新增元素的時(shí)候,我們把參數(shù)提取出來,然后使用observeArray對(duì)新增的數(shù)據(jù)進(jìn)行變化偵測(cè),對(duì)于數(shù)組,只能攔截原型方法,對(duì)于一些特有的方法就無法攔截。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
淺談Vue.nextTick 的實(shí)現(xiàn)方法
本篇文章主要介紹了Vue.nextTick 的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10vue3實(shí)現(xiàn)點(diǎn)擊空白區(qū)域隱藏div
這篇文章主要介紹了vue3實(shí)現(xiàn)點(diǎn)擊空白區(qū)域隱藏div方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04vue選項(xiàng)卡Tabs組件實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了vue選項(xiàng)卡Tabs組件實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Element-Ui組件 NavMenu 導(dǎo)航菜單的具體使用
這篇文章主要介紹了Element-Ui組件 NavMenu 導(dǎo)航菜單的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10在vue中使用echarts實(shí)現(xiàn)上浮與下鉆效果
這篇文章主要介紹了在vue中使用echarts實(shí)現(xiàn)上浮與下鉆效果,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11Vue實(shí)現(xiàn)父子組件傳值其實(shí)不難
這篇文章主要介紹了Vue實(shí)現(xiàn)父子組件傳值其實(shí)不難問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03