日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > vue >内容正文

vue

Vue:Array变化侦测

發(fā)布時(shí)間:2024/3/13 vue 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Vue:Array变化侦测 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Vue:Array變化偵測(cè)

1. 數(shù)組追蹤變化

? 與Object不同,數(shù)組無(wú)法通過(guò)getter和setter方式來(lái)追蹤變化,因此,我們需要自定義一個(gè)攔截器來(lái)追蹤變化。

2. 攔截器的準(zhǔn)備

? 攔截器其實(shí)就是一個(gè)與Array.prototype一樣的Object,里面所包含的屬性一樣,但是改變數(shù)組的方法是我們修改過(guò)的。

? 首先,我們先對(duì)Array.prototype來(lái)個(gè)定制。

const arrayProto = Array.prototype;const arrayMethods = Object.create(arrayProto); let methods = ['push','pop','shift','unshift','splice','sort','reverse'];methods.forEach(function(method){// 獲取原始方法const original = arrayMethods[method];Object.defineProperty(arrayMethods,method,{value: function mutator(...args){return original.apply(this,args);},enumerable: true,writable: true,configurable: true}) })
  • 上面代碼中,我們對(duì)Array.prototype進(jìn)行了拷貝,創(chuàng)建出新的對(duì)象arrayMethods,其中的mutator就是我們追蹤變化的關(guān)鍵,未來(lái)將對(duì)其進(jìn)行擴(kuò)展。

3. 使用攔截器覆蓋Array原型

? 要將一個(gè)數(shù)據(jù)轉(zhuǎn)化成響應(yīng)式的數(shù)據(jù),需要通過(guò)Observer,在Observer里使用攔截器覆蓋那些即將被轉(zhuǎn)換成響應(yīng)式的Array類(lèi)型數(shù)據(jù):

const hasProto = '__proto__' in {}; // 檢測(cè)是否可用proto const arrayKeys = Object.getOwnPropertyNames(arrayMethods); class Observer{constructor(value){this.value = value;if(Array.isArray(value){// 根據(jù)是否支持ES6來(lái)對(duì)原型對(duì)象進(jìn)行覆蓋const arguments = hasProto? 'protoArguments' : 'copyArguments';arguments(value,arrayMethods,arrayKeys);}else{this.walk(value); })}...... }function protoArguments(target,src,keys){target.__proto__ = src; } // 遞歸復(fù)制屬性 function copyArguments(target,src,keys){for(let i=0;i<keys.length;i++){const key = keys[i];def(target,key,src[key]); // 將arrayMethods的方法添加到target中} }

4. 收集依賴(lài)

? 我們先來(lái)回顧一下收集依賴(lài)的類(lèi)Dep:

class Dep{constructor(){this.subs = [];}addSub(sub){this.subs.push(sub);}removeSub(sub){let index = this.subs.indexOf(sub);if(index > -1){this.subs = this.subs.splice(index,1);}}depend(){if(window.target){this.addSub(window.target);}}notify(){let subs = this.subs.splice();for(let i=0;i<subs.length;i++){subs[i].update();}} }

? 與Object不一樣的是,Array的依賴(lài)保存在Observer中,原因是這樣做既能在getter中訪(fǎng)問(wèn)到Observer實(shí)例,又能在攔截器中訪(fǎng)問(wèn)到Observer實(shí)例。

class Observer{constructor(value){this.value = value;this.dep = new Dep();if(Array.isArray(value)){// 這里不再贅述,直接用ES6語(yǔ)法value.__proto__ = arrayMethods;}else{this.walk(value);}}...... }

? Array的收集依賴(lài)也是在defineReactive中收集的,當(dāng)我們把依賴(lài)保存到Observer后,我們可以在getter中對(duì)其進(jìn)行收集:

function defineReactive(data,key,val){let childOb = observe(val); //為val創(chuàng)建Observer實(shí)例let dep = new Dep();Object.defineProperty(data,key,{enumerable: true,configurable: true,get:function(){dep.depend();// 收集依賴(lài)if(childOb){childOb.dep.depend();}return val;},set:function(newVal){if(val === newVal){return ;}dep.notify();val = newVal;}}) }/*為val創(chuàng)建一個(gè)Observer實(shí)例,創(chuàng)建成功返回新的Observer實(shí)例,若val已存在一個(gè),則返回val */ function observe(val,asRootData){if(typeof val !== 'object'){return ;}let ob = null;if(hasOwn(val,'__ob__') && val.__ob__.instanceof Observer){ob = val.__ob__;}else{ob = new Observer(val);}return ob; }

5. 在攔截器中獲取Observer實(shí)例

? 由于Array攔截器是對(duì)原型的封裝,因此可以在攔截器中訪(fǎng)問(wèn)到當(dāng)前正在操作數(shù)組的this:

function def(obj,key,val,enumerable){Object.defineProperty(obj,key,{value: val,enumerable: !!enumerable,writable: true,configurable: true}) }class Observer{constructor(value){this.value = value;this.dep = new Dep();def(value,'__ob__',this); // 在value上新增一個(gè)不可枚舉的屬性__ob__,即當(dāng)前Observer實(shí)例value.__proto__ = arrayMethods;}....... }

? 通過(guò)上面的方式,我們就可以通過(guò)__ob__來(lái)拿到Observer實(shí)例了,進(jìn)而拿到dep實(shí)例。

? __ob__的作用還可以用來(lái)記錄當(dāng)前value是否被Observer轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)。如果擁有ob屬性,則說(shuō)明他們是響應(yīng)式的;如果沒(méi)有,則通過(guò)new Observer將其轉(zhuǎn)換成響應(yīng)式的。

? 由于攔截器是原型方法,所以可以通過(guò)this.__ob__來(lái)訪(fǎng)問(wèn)Observer實(shí)例。

let methods = ['push','pop','shift','unshift','splice','sort','reverse'];methods.forEach(function(method){// 獲取原始方法const original = arrayMethods[method];Object.defineProperty(arrayMethods,method,{value: function mutator(...args){const ob = this.__ob__;return original.apply(this,args);},enumerable: true,writable: true,configurable: true}) })

6. 向數(shù)組依賴(lài)發(fā)送通知

? 想要發(fā)送通知,必須得先獲取依賴(lài),然后直接調(diào)用依賴(lài)的發(fā)送通知方法:

let methods = ['push','pop','shift','unshift','splice','sort','reverse'];methods.forEach(function(method){// 獲取原始方法const original = arrayMethods[method];def(arrayMethods,method,function mutator(...args){const result = original.apply(this,args);const ob = this.__ob__;ob.dep.notify(); // 發(fā)送通知return result;}) })

7. 偵測(cè)數(shù)組中元素的變化

? 在這之前我們偵測(cè)的變化,指的是數(shù)組自身的變化,包括新增或刪除一個(gè)元素;那么,要如何偵測(cè)數(shù)組中每一項(xiàng)元素的改變呢?

class Observer{constructor(value){this.value = value;def(value,'__ob__',this);if(Array.isArray(value)){this.observeArray(value);}else{this.walk(value);}}// 偵測(cè)數(shù)組內(nèi)的每一項(xiàng)數(shù)據(jù)observeArray(items){for(let i=0;i<items.length;i++){observe(items[i]);}}...... }

? 上面的代碼中,我們對(duì)數(shù)組中每一項(xiàng)都執(zhí)行了一遍new Observer,即全部元素都轉(zhuǎn)化成響應(yīng)式的。

8. 偵測(cè)新增元素

? 要偵測(cè)新增元素,首先得獲取新增元素,我們可以使用Observer來(lái)實(shí)現(xiàn):

let methods = ['push','pop','shift','unshift','splice','sort','reverse'];methods.forEach(function(method){// 獲取原始方法const original = arrayMethods[method];def(arrayMethods,method,function mutator(...args){const result = original.apply(this,args);const ob = this.__ob__;let inserted;switch(method){case 'push':case 'unshift':inserted = args;break;case 'splice':inserted = args.slice(2);break;}// 將新增元素轉(zhuǎn)化成響應(yīng)式if(inserted) ob.observeArray(inserted);ob.dep.notify(); // 發(fā)送通知return result;}) })

9. 總結(jié)

  • Array與Object的追蹤方式不同。Array是通過(guò)方法來(lái)改變內(nèi)容的,因此我們需要?jiǎng)?chuàng)建攔截器去覆蓋數(shù)組原型的方式來(lái)追蹤變化;

  • 為了不污染全局Array.prototype,我們?cè)贠bserver只針對(duì)需要偵測(cè)變化的數(shù)組,使用ES6的__proto__屬性來(lái)覆蓋其原型方法;對(duì)于不支持ES6語(yǔ)法的瀏覽器,我們直接循環(huán)攔截器,將所有方法直接設(shè)置到數(shù)組身上來(lái)攔截?cái)?shù)組原型的方法;

  • Array的依賴(lài)保存在Observer實(shí)例上;

  • 在Observer中,我們對(duì)數(shù)組每一項(xiàng)元素都做了偵測(cè)并印上了標(biāo)記__ob__,并把this保存起來(lái),目的是:一是標(biāo)記數(shù)據(jù)已經(jīng)被偵測(cè)化;二是可以很方便拿到__ob__,從而拿到保存在Observer的依賴(lài)。方便后續(xù)通知。

  • 對(duì)于新增數(shù)據(jù),我們對(duì)當(dāng)前操作數(shù)組的方法進(jìn)行判斷,如果為’push’,‘unshift’,‘splice’,我們就從參數(shù)中將數(shù)據(jù)提取出來(lái),然后使用observeArray方法對(duì)其進(jìn)行數(shù)據(jù)偵測(cè)。

總結(jié)

以上是生活随笔為你收集整理的Vue:Array变化侦测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。