<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

        詳解Vue 如何監(jiān)聽Array的變化

        來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 21:55:42
        文檔

        詳解Vue 如何監(jiān)聽Array的變化

        詳解Vue 如何監(jiān)聽Array的變化:回憶 在上一篇Vue響應(yīng)式原理-理解Observer、Dep、Watcher簡(jiǎn)單講解了Observer、Dep、Watcher三者的關(guān)系。 在Observer的偽代碼中我們模擬了如下代碼: class Observer { constructor() { // 響應(yīng)式綁定數(shù)據(jù)通過(guò)方法 observe(thi
        推薦度:
        導(dǎo)讀詳解Vue 如何監(jiān)聽Array的變化:回憶 在上一篇Vue響應(yīng)式原理-理解Observer、Dep、Watcher簡(jiǎn)單講解了Observer、Dep、Watcher三者的關(guān)系。 在Observer的偽代碼中我們模擬了如下代碼: class Observer { constructor() { // 響應(yīng)式綁定數(shù)據(jù)通過(guò)方法 observe(thi

        回憶

        在上一篇Vue響應(yīng)式原理-理解Observer、Dep、Watcher簡(jiǎn)單講解了Observer、Dep、Watcher三者的關(guān)系。

        在Observer的偽代碼中我們模擬了如下代碼:

        class Observer {
         constructor() {
         // 響應(yīng)式綁定數(shù)據(jù)通過(guò)方法
         observe(this.data);
         }
        }
        
        export function observe (data) {
         const keys = Object.keys(data);
         for (let i = 0; i < keys.length; i++) {
         // 將data中我們定義的每個(gè)屬性進(jìn)行響應(yīng)式綁定
         defineReactive(obj, keys[i]);
         }
        }
        
        export function defineReactive () {
         // ...省略 Object.defineProperty get-set
        }
        
        

        今天我們就進(jìn)一步了解Observer里還做了什么事。

        Array的變化如何監(jiān)聽?

        data 中的數(shù)據(jù)如果是一個(gè)數(shù)組怎么辦?我們發(fā)現(xiàn)Object.defineProperty對(duì)數(shù)組進(jìn)行響應(yīng)式化是有缺陷的。

        雖然我們可以監(jiān)聽到索引的改變。

        function defineReactive (obj, key, val) {
         Object.defineProperty(obj, key, {
         enumerable: true,
         configurable: true,
         get: () => {
         console.log('我被讀了,我要不要做點(diǎn)什么好?');
         return val;
         },
         set: newVal => {
         if (val === newVal) {
         return;
         }
         val = newVal;
         console.log("數(shù)據(jù)被改變了,我要渲染到頁(yè)面上去!");
         }
         })
        }
        
        let data = [1];
        
        // 對(duì)數(shù)組key進(jìn)行監(jiān)聽
        defineReactive(data, 0, 1);
        console.log(data[0]); // 我被讀了,我要不要做點(diǎn)什么好?
        data[0] = 2; // 數(shù)據(jù)被改變了,我要渲染到頁(yè)面上去!
        
        

        但是defineProperty不能檢測(cè)到數(shù)組長(zhǎng)度的變化,準(zhǔn)確的說(shuō)是通過(guò)改變length而增加的長(zhǎng)度不能監(jiān)測(cè)到。這種情況無(wú)法觸發(fā)任何改變。

        data.length = 0; // 控制臺(tái)沒(méi)有任何
        輸出

        而且監(jiān)聽數(shù)組所有索引的的代價(jià)也比較高,綜合一些其他因素,Vue用了另一個(gè)方案來(lái)處理。

        首先我們的observe需要改造一下,單獨(dú)加一個(gè)數(shù)組的處理。

        // 將data中我們定義的每個(gè)屬性進(jìn)行響應(yīng)式綁定
        export function observe (data) {
         const keys = Object.keys(data);
         for (let i = 0; i < keys.length; i++) {
         // 如果是數(shù)組
         if (Array.isArray(keys[i])) {
         observeArray(keys[i]);
         } else {
         // 如果是對(duì)象
         defineReactive(obj, keys[i]);
         }
         }
        }
        
        // 數(shù)組的處理
        export function observeArray () {
         // ...省略
        }
        
        

        那接下來(lái)我們就應(yīng)該考慮下Array變化如何監(jiān)聽?

        Vue 中對(duì)這個(gè)數(shù)組問(wèn)題的解決方案非常的簡(jiǎn)單粗暴,就是對(duì)能夠改變數(shù)組的方法做了一些手腳。

        我們知道,改變數(shù)組的方法有很多,舉個(gè)例子比如說(shuō)push方法吧。push存在Array.prototype上的,如果我們能
        能攔截到原型上的push方法,是不是就可以做一些事情呢?

        Object.defineProperty

        對(duì)象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符。存取描述符是由getter-setter函數(shù)對(duì)描述的屬性,也就是我們用來(lái)給對(duì)象做響應(yīng)式綁定的。Object.defineProperty-MDN

        雖然我們無(wú)法使用Object.defineProperty將數(shù)組進(jìn)行響應(yīng)式的處理,也就是getter-setter,但是還有其他的功能可以供我們使用。就是數(shù)據(jù)描述符,數(shù)據(jù)描述符是一個(gè)具有值的屬性,該值可能是可寫的,也可能不是可寫的。

        value

        該屬性對(duì)應(yīng)的值??梢允侨魏斡行У?JavaScript 值(數(shù)值,對(duì)象,函數(shù)等)。默認(rèn)為 undefined。

        writable

        當(dāng)且僅當(dāng)該屬性的writable為true時(shí),value才能被賦值運(yùn)算符改變。默認(rèn)為 false。

        因此我們只要把原型上的方法,進(jìn)行value的重新賦值。

        如下代碼,在重新賦值的過(guò)程中,我們可以獲取到方法名和所有參數(shù)。

        function def (obj, key) {
         Object.defineProperty(obj, key, {
         writable: true,
         enumerable: true,
         configurable: true,
         value: function(...args) {
         console.log('key', key);
         console.log('args', args); 
         }
         });
        }
        
        // 重寫的數(shù)組方法
        let obj = {
         push() {}
        }
        
        // 數(shù)組方法的綁定
        def(obj, 'push');
        
        obj.push([1, 2], 7, 'hello!');
        // 控制臺(tái)
        輸出 key push // 控制臺(tái)輸出 args [Array(2), 7, "hello!"]

        通過(guò)如上代碼我們就可以知道,用戶使用了數(shù)組上原型的方法以及參數(shù)我們都可以攔截到,這個(gè)攔截的過(guò)程就可以做一些變化的通知。

        Vue監(jiān)聽Array三步曲

        接下來(lái),就看看Vue是如何實(shí)現(xiàn)的吧~

        第一步:先獲取原生 Array 的原型方法,因?yàn)閿r截后還是需要原生的方法幫我們實(shí)現(xiàn)數(shù)組的變化。

        第二步:對(duì) Array 的原型方法使用 Object.defineProperty 做一些攔截操作。

        第三步:把需要被攔截的 Array 類型的數(shù)據(jù)原型指向改造后原型。

        我們將代碼進(jìn)行下改造,攔截的過(guò)程中還是要將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變,然后我們?cè)偃プ鲆晥D的更新等操作。

        const arrayProto = Array.prototype // 獲取Array的原型
        
        function def (obj, key) {
         Object.defineProperty(obj, key, {
         enumerable: true,
         configurable: true,
         value: function(...args) {
         console.log(key); // 控制臺(tái)
        輸出 push console.log(args); // 控制臺(tái)輸出 [Array(2), 7, "hello!"] // 獲取原生的方法 let original = arrayProto[key]; // 將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變 const result = original.apply(this, args); // do something 比如通知Vue視圖進(jìn)行更新 console.log('我的數(shù)據(jù)被改變了,視圖該更新啦'); this.text = 'hello Vue'; return result; } }); } // 新的原型 let obj = { push() {} } // 重寫賦值 def(obj, 'push'); let arr = [0]; // 原型的指向重寫 arr.__proto__ = obj; // 執(zhí)行push arr.push([1, 2], 7, 'hello!'); console.log(arr);

        被改變后的arr。

        Vue源碼解析

        array.js

        Vue在array.js中重寫了methodsToPatch中七個(gè)方法,并將重寫后的原型暴露出去。

        // Object.defineProperty的封裝
        import { def } from '../util/index'
        
        // 獲得原型上的方法
        const arrayProto = Array.prototype
        
        // Vue攔截的方法
        const methodsToPatch = [
         'push',
         'pop',
         'shift',
         'unshift',
         'splice',
         'sort',
         'reverse'
        ];
        
        // 將上面的方法重寫
        methodsToPatch.forEach(function (method) {
         def(arrayMethods, method, function mutator (...args) {
         console.log('method', method); // 獲取方法
         console.log('args', args); // 獲取參數(shù)
        
         // ...功能如上述,監(jiān)聽到某個(gè)方法執(zhí)行后,做一些對(duì)應(yīng)的操作
         // 1、將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變
         // 2、視圖更新等
         })
        })
        
        export const arrayMethods = Object.create(arrayProto);
        
        

        observer

        在進(jìn)行數(shù)據(jù)observer綁定的時(shí)候,我們先判斷是否hasProto,如果存在__proto__,就直接將value 的 __proto__指向重寫過(guò)后的原型。如果不能使用 __proto__,貌似有些瀏覽器廠商沒(méi)有實(shí)現(xiàn)。那就直接循環(huán) arrayMethods把它身上的這些方法直接裝到 value 身上好了。畢竟調(diào)用某個(gè)方法是先去自身查找,當(dāng)自身找不到這關(guān)方法的時(shí)候,才去原型上查找。

        // 判斷是否有__proto__,因?yàn)椴糠譃g覽器是沒(méi)有__proto__
        const hasProto = '__proto__' in {}
        // 重寫后的原型
        import { arrayMethods } from './array'
        // 方法名
        const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
        
        // 數(shù)組的處理
        export function observeArray (value) {
         // 如果有__proto__,直接覆蓋 
         if (hasProto) {
         protoAugment(value, arrayMethods);
         } else {
         // 沒(méi)有__proto__就把方法加到屬性自身上
         copyAugment(value, arrayMethods, )
         }
        }
        
        // 原型的賦值
        function protoAugment (target, src) {
         target.__proto__ = src;
        }
        
        // 復(fù)制
        function copyAugment (target, src, keys) {
         for (let i = 0, l = keys.length; i < l; i++) {
         const key = keys[i]
         def(target, key, src[key]);
         }
        }
        
        

        通過(guò)上面的代碼我們發(fā)現(xiàn),沒(méi)有直接修改 Array.prototype,而是直接把 arrayMenthods 賦值給 value 的 __proto__ 。因?yàn)檫@樣不會(huì)污染全局的Array, arrayMenthods 只對(duì) data中的Array 生效。

        總結(jié)

        因?yàn)楸O(jiān)聽的數(shù)組帶來(lái)的代價(jià)和一些問(wèn)題,Vue使用了重寫原型的方案代替。攔截了數(shù)組的一些方法,在這個(gè)過(guò)程中再去做通知變化等操作。

        本文的一些代碼均是Vue源碼簡(jiǎn)化后的,為了方便大家理解。思想理解了,源碼就容易看懂了。

        聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        詳解Vue 如何監(jiān)聽Array的變化

        詳解Vue 如何監(jiān)聽Array的變化:回憶 在上一篇Vue響應(yīng)式原理-理解Observer、Dep、Watcher簡(jiǎn)單講解了Observer、Dep、Watcher三者的關(guān)系。 在Observer的偽代碼中我們模擬了如下代碼: class Observer { constructor() { // 響應(yīng)式綁定數(shù)據(jù)通過(guò)方法 observe(thi
        推薦度:
        標(biāo)簽: VUE 的變化 數(shù)組的
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top 主站蜘蛛池模板: 看一级毛片免费观看视频| 国产人成亚洲第一网站在线播放| 国产亚洲精品精品精品| 女人18毛片水真多免费看| 亚洲精品乱码久久久久久蜜桃图片 | 亚洲国产一成久久精品国产成人综合 | 五月天婷婷精品免费视频| 亚洲?V无码乱码国产精品| 男女啪啪免费体验区| 国产精品亚洲一区二区三区在线 | 阿v免费在线观看| 国产亚洲精品国看不卡| 中国一级特黄高清免费的大片中国一级黄色片 | 在线观看成人免费视频不卡| 亚洲videos| 性做久久久久免费看| 无码 免费 国产在线观看91| 国产亚洲情侣一区二区无码AV| 国产在线精品免费aaa片| 久久亚洲AV无码精品色午夜麻豆 | 亚洲av片在线观看| 91麻豆精品国产自产在线观看亚洲| 久久99免费视频| 亚洲videosbestsex日本| 亚洲Aⅴ无码一区二区二三区软件| 搜日本一区二区三区免费高清视频 | 69式国产真人免费视频 | 内射干少妇亚洲69XXX| 91在线视频免费看| 理论片在线观看免费| 亚洲AV日韩AV鸥美在线观看| 在线视频免费观看高清| 日韩大片免费观看视频播放| 亚洲AV永久无码精品水牛影视 | 免费一级特黄特色大片在线 | a毛片在线免费观看| 亚洲人成网站看在线播放| 国产综合精品久久亚洲| 精品女同一区二区三区免费站 | 无码人妻一区二区三区免费n鬼沢| 亚洲最大福利视频|