最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue2?響應(yīng)式系統(tǒng)之?dāng)?shù)組

 更新時(shí)間:2022年04月12日 20:33:41   作者:windliang  
這篇文章主要介紹了Vue2?響應(yīng)式系統(tǒng)之?dāng)?shù)組,本文接著上幾篇文章Vue2響應(yīng)式系統(tǒng)?、Vue2?響應(yīng)式系統(tǒng)之分支切換,響應(yīng)式系統(tǒng)之嵌套、響應(yīng)式系統(tǒng)之深度響應(yīng)?展開(kāi)相關(guān)內(nèi)容,需要的朋友可以參考一下

本文接Vue2響應(yīng)式系統(tǒng) 、Vue2 響應(yīng)式系統(tǒng)之分支切換  ,響應(yīng)式系統(tǒng)之嵌套、響應(yīng)式系統(tǒng)之深度響應(yīng) 還沒(méi)有看過(guò)的小伙伴需要看一下。

1、場(chǎng)景

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};

new Watcher(updateComponent);
data.list = ["hello", "liang"];

先可以一分鐘思考下會(huì)輸出什么。

雖然 的值是數(shù)組,但我們是對(duì) 進(jìn)行整體賦值,所以依舊會(huì)觸發(fā) 的 ,觸發(fā) 進(jìn)行重新執(zhí)行,輸出如下:listdata.listdata.listsetWatcher

image-20220405184002118

2、場(chǎng)景 2

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};
new Watcher(updateComponent);

data.list.push("liang");

先可以一分鐘思考下會(huì)輸出什么。

這次是調(diào)用 方法,但我們對(duì) 方法什么都沒(méi)做,因此就不會(huì)觸發(fā) 了。pushpushWatcher

3、方案

為了讓 還有數(shù)組的其他方法也生效,我們需要去重寫(xiě)它們,通過(guò)push代理模式 我們可以將數(shù)組的原方法先保存起來(lái),然后執(zhí)行,并且加上自己額外的操作。

/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

/*
export function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true,
    });
}
*/
import { def } from "./util";

const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
    // cache original method
    const original = arrayProto[method];
    def(arrayMethods, method, function mutator(...args) {
        const result = original.apply(this, args);
        /*****************這里相當(dāng)于調(diào)用了對(duì)象 set 需要通知 watcher ************************/
        // 待補(bǔ)充
        /**************************************************************************** */
        return result;
    });
});

當(dāng)調(diào)用了數(shù)組的 或者其他方法,就相當(dāng)于我們之前重寫(xiě)屬性的 ,上邊待補(bǔ)充的地方需要做的就是通知 中的 。pushsetdepWatcher

export function defineReactive(obj, key, val, shallow) {
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 讀取用戶(hù)可能自己定義了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 沒(méi)有傳進(jìn)來(lái)話進(jìn)行手動(dòng)賦值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一個(gè) Dep 對(duì)象,用來(lái)保存所有依賴(lài)于該變量的 Watcher

    let childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            dep.notify();
        },
    });
}

如上邊的代碼,之前的 是通過(guò)閉包,每一個(gè)屬性都有一個(gè)各自的 ,負(fù)責(zé)收集 和通知 。depdepWatcherWatcher

那么對(duì)于數(shù)組的話,我們的 放到哪里比較簡(jiǎn)單呢?dep

回憶一下現(xiàn)在的結(jié)構(gòu)。

const data = {
    list: ["hello"],
};
observe(data);

const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};
new Watcher(updateComponent);

上邊的代碼執(zhí)行過(guò)后會(huì)是下圖的結(jié)構(gòu)。

image-20220405200509660

list 屬性在閉包中擁有了 屬性,通過(guò) ,收集到了包含 函數(shù)的 。Depnew WatcherupdateCompnentWatcher

同時(shí)因?yàn)?nbsp;的 是數(shù)組,也就是對(duì)象,通過(guò)上篇 listvalue["hello"]響應(yīng)式系統(tǒng)之深度響應(yīng) (opens new window)我們知道,它也會(huì)去調(diào)用 函數(shù)。Observer

那么,我是不是在 中也加一個(gè) 就可以了。ObserverDep

image-20220405201451179

這樣當(dāng)我們調(diào)用數(shù)組方法去修改 的值的時(shí)候,去通知 中的 就可以了。['hello']ObserverDep

3、收集依賴(lài)代碼實(shí)現(xiàn)

按照上邊的思路,完善一下 類(lèi)。Observer

export class Observer {
    constructor(value) {
        /******新增 *************************/
        this.dep = new Dep();
        /************************************/
      	this.walk(value);
    }

    /**
     * 遍歷對(duì)象所有的屬性,調(diào)用 defineReactive
     * 攔截對(duì)象屬性的 get 和 set 方法
     */
    walk(obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
        }
    }
}

然后在 中,當(dāng)前 中的 也去收集依賴(lài)。getOberverdep

export function defineReactive(obj, key, val, shallow) {
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 讀取用戶(hù)可能自己定義了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 沒(méi)有傳進(jìn)來(lái)話進(jìn)行手動(dòng)賦值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一個(gè) Dep 對(duì)象,用來(lái)保存所有依賴(lài)于該變量的 Watcher

    let childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
                /******新增 *************************/
                if (childOb) {
                    // 當(dāng)前 value 是數(shù)組,去收集依賴(lài)
                    if (Array.isArray(value)) {
                        childOb.dep.depend();
                    }
                }
                /************************************/
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            dep.notify();
        },
    });
}

4、通知依賴(lài)代碼實(shí)現(xiàn)

我們已經(jīng)重寫(xiě)了 方法,但直接覆蓋全局的 方法肯定是不好的,我們可以在 類(lèi)中去操作,如果當(dāng)前 是數(shù)組,就去攔截它的 方法。arrayarrrayObservervaluearray

這里就回到 的原型鏈上了,我們可以通過(guò)瀏覽器自帶的 ,將當(dāng)前對(duì)象的原型指向我們重寫(xiě)過(guò)的方法即可。js__proto__

考慮兼容性的問(wèn)題,如果 不存在,我們直接將重寫(xiě)過(guò)的方法復(fù)制給當(dāng)前對(duì)象即可。__proto__

import { arrayMethods } from './array' // 上邊重寫(xiě)的所有數(shù)組方法
/* export const hasProto = "__proto__" in {}; */
export class Observer {
    constructor(value) {
        this.dep = new Dep();
      	/******新增 *************************/
        if (Array.isArray(value)) {
            if (hasProto) {
                protoAugment(value, arrayMethods);
            } else {
                copyAugment(value, arrayMethods, arrayKeys);
            }
        /************************************/
        } else {
            this.walk(value);
        }
    }

    /**
     * 遍歷對(duì)象所有的屬性,調(diào)用 defineReactive
     * 攔截對(duì)象屬性的 get 和 set 方法
     */
    walk(obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
        }
    }
}
/**
 * Augment a target Object or Array by intercepting
 * the prototype chain using __proto__
 */
function protoAugment(target, src) {
    /* eslint-disable no-proto */
    target.__proto__ = src;
    /* eslint-enable no-proto */
}

/**
 * Augment a target Object or Array by defining
 * hidden properties.
 */
/* istanbul ignore next */
function copyAugment(target, src, keys) {
    for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i];
        def(target, key, src[key]);
    }
}

還需要考慮一點(diǎn),數(shù)組方法中我們只能拿到 值,那么怎么拿到 對(duì)應(yīng)的 呢。valuevalueObserver

我們只需要在 類(lèi)中,增加一個(gè)屬性來(lái)指向自身即可。Observe

export class Observer {
    constructor(value) {
        this.dep = new Dep();
        /******新增 *************************/
        def(value, '__ob__', this)
        /************************************/
        if (Array.isArray(value)) {
            if (hasProto) {
                protoAugment(value, arrayMethods);
            } else {
                copyAugment(value, arrayMethods, arrayKeys);
            }
        } else {
            this.walk(value);
        }
    }
  	...
}

回到最開(kāi)始重寫(xiě)的 方法中,只需要從 中拿到 去通知 即可。array__ob__DepWatcher

/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from "./util";

const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",
];

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
    // cache original method
    const original = arrayProto[method];
    def(arrayMethods, method, function mutator(...args) {
        const result = original.apply(this, args);
        /*****************這里相當(dāng)于調(diào)用了對(duì)象 set 需要通知 watcher ************************/
        const ob = this.__ob__;
        // notify change
        ob.dep.notify();
        /**************************************************************************** */
        return result;
    });
});

5、測(cè)試

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    list: ["hello"],
};
observe(data);
const updateComponent = () => {
    for (const item of data.list) {
        console.log(item);
    }
};

new Watcher(updateComponent);
data.list.push("liang");

這樣當(dāng)調(diào)用 方法的時(shí)候,就會(huì)觸發(fā)相應(yīng)的 來(lái)執(zhí)行 函數(shù)了。pushWatcherupdateComponent

當(dāng)前的依賴(lài)就變成了下邊的樣子:

image-20220405205545348

6、總結(jié)

對(duì)于數(shù)組的響應(yīng)式我們解決了三個(gè)問(wèn)題,依賴(lài)放在哪里、收集依賴(lài)和通知依賴(lài)。

我們來(lái)和普通對(duì)象屬性進(jìn)行一下對(duì)比。

image-20220405210847016

 到此這篇關(guān)于Vue2 響應(yīng)式系統(tǒng)之?dāng)?shù)組的文章就介紹到這了,更多相關(guān)Vue2 數(shù)組內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

安陆市| 彭泽县| 靖安县| 永济市| 广汉市| 洛阳市| 镇坪县| 邹城市| 芜湖县| 霍州市| 蕉岭县| 西充县| 新和县| 浠水县| 信丰县| 钟祥市| 临朐县| 营口市| 吴江市| 上栗县| 文成县| 哈巴河县| 瑞丽市| 民勤县| 扎囊县| 湛江市| 马山县| 鄂伦春自治旗| 灯塔市| 甘德县| 永平县| 定南县| 灵寿县| 察哈| 临泽县| 邹城市| 武强县| 镇沅| 自贡市| 兴国县| 浪卡子县|