成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Vue2剝絲抽繭-響應式系統之數組

開發 前端
我們已經重寫了 array 方法,但直接覆蓋全局的 arrray 方法肯定是不好的,我們可以在 Observer 類中去操作,如果當前 value 是數組,就去攔截它的 array 方法。

場景

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"];

先可以一分鐘思考下會輸出什么。

... ...

雖然 list 的值是數組,但我們是對 data.list 進行整體賦值,所以依舊會觸發 data.list 的 set ,觸發 Watcher 進行重新執行,輸出如下:

場景 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");

先可以一分鐘思考下會輸出什么。

... ...

這次是調用 push 方法,但我們對 push 方法什么都沒做,因此就不會觸發 Watcher 了。

方案

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

/*
* 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);
/*****************這里相當于調用了對象 set 需要通知 watcher ************************/
// 待補充
/**************************************************************************** */
return result;
});
});

當調用了數組的 push 或者其他方法,就相當于我們之前重寫屬性的 set ,上邊待補充的地方需要做的就是通知 dep 中的 Watcher 。

export function defineReactive(obj, key, val, shallow) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 讀取用戶可能自己定義了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 沒有傳進來話進行手動賦值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
const dep = new Dep(); // 持有一個 Dep 對象,用來保存所有依賴于該變量的 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();
},
});
}

如上邊的代碼,之前的 dep 是通過閉包,每一個屬性都有一個各自的 dep ,負責收集 Watcher 和通知 Watcher 。

那么對于數組的話,我們的 dep 放到哪里比較簡單呢?

回憶一下現在的結構。

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

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

上邊的代碼執行過后會是下圖的結構。

list 屬性在閉包中擁有了 Dep 屬性,通過 new Watcher ,收集到了包含 updateCompnent 函數的 Watcher。

同時因為 list 的 value ["hello"] 是數組,也就是對象,通過上篇 響應式系統之深度響應 我們知道,它也會去調用 Observer 函數。

那么,我是不是在 Observer 中也加一個 Dep 就可以了。

這樣當我們調用數組方法去修改 ['hello'] 的值的時候,去通知 Observer 中的 Dep 就可以了。

收集依賴代碼實現

按照上邊的思路,完善一下 Observer 類。

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

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

然后在 get 中,當前 Oberver 中的 dep 也去收集依賴。

export function defineReactive(obj, key, val, shallow) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 讀取用戶可能自己定義了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 沒有傳進來話進行手動賦值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
const dep = new Dep(); // 持有一個 Dep 對象,用來保存所有依賴于該變量的 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) {
// 當前 value 是數組,去收集依賴
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();
},
});
}

通知依賴代碼實現

我們已經重寫了 array 方法,但直接覆蓋全局的 arrray 方法肯定是不好的,我們可以在 Observer 類中去操作,如果當前 value 是數組,就去攔截它的 array 方法。

這里就回到 js 的原型鏈上了,我們可以通過瀏覽器自帶的 __proto__ ,將當前對象的原型指向我們重寫過的方法即可。

考慮兼容性的問題,如果 __proto__ 不存在,我們直接將重寫過的方法復制給當前對象即可。

import { arrayMethods } from './array' // 上邊重寫的所有數組方法
/* 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);
}
}

/**
* 遍歷對象所有的屬性,調用 defineReactive
* 攔截對象屬性的 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]);
}
}

還需要考慮一點,數組方法中我們只能拿到 value 值,那么怎么拿到 value 對應的 Observer 呢。

我們只需要在 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);
}
}
...
}

回到最開始重寫的 array 方法中,只需要從 __ob__ 中拿到 Dep 去通知 Watcher 即可。

/*
* 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);
/*****************這里相當于調用了對象 set 需要通知 watcher ************************/
const ob = this.__ob__;
// notify change
ob.dep.notify();
/**************************************************************************** */
return result;
});
});

測試

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");

這樣當調用 push 方法的時候,就會觸發相應的 Watcher 來執行 updateComponent 函數了。

當前的依賴就變成了下邊的樣子:

總結

對于數組的響應式我們解決了三個問題,依賴放在哪里、收集依賴和通知依賴。

我們來和普通對象屬性進行一下對比。

責任編輯:武曉燕 來源: windliang
相關推薦

2022-03-29 09:59:58

響應式系統Vue2

2022-04-02 09:56:41

Vue2響應式系統

2022-04-14 08:46:46

響應式系統js

2022-04-03 19:27:35

Vue2響應式系統

2022-04-12 10:05:18

響應式系統異步隊列

2022-03-31 10:15:10

分支切換響應式系統

2022-04-10 11:04:40

響應式系統setdelete

2022-08-31 08:09:35

Vue2AST模版

2023-03-02 11:51:00

數據分析師企業

2024-03-07 12:54:06

數據分析師企業

2024-09-02 16:10:19

vue2前端

2019-04-25 14:20:56

數據分析套路工具

2021-05-19 14:25:19

前端開發技術

2022-06-26 00:00:02

Vue3響應式系統

2024-03-15 11:47:19

Vue2前端權限控制

2021-03-09 22:29:46

Vue 響應式API

2023-11-19 18:53:27

Vue2MVVM

2016-10-19 20:47:55

vuevue-cli移動端

2020-09-25 07:40:39

技術開發選型

2020-06-09 11:35:30

Vue 3響應式前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费av毛片 | 欧美2区 | 九九热在线免费视频 | 国产高清视频 | 亚洲精品一区久久久久久 | 欧美一级片在线播放 | 综合网中文字幕 | 亚洲精品中文字幕在线观看 | 颜色网站在线观看 | 欧美九九 | 久久久精品影院 | 免费观看av | 国产成人综合亚洲欧美94在线 | 久久国产精品首页 | 国产精品污www一区二区三区 | 91资源在线| 欧美日韩成人一区二区 | 国产日韩欧美 | 国产精品a级 | 91xxx在线观看| 精品欧美一区二区三区久久久 | 久久精品亚洲国产奇米99 | 日韩精品成人 | 中文字幕av在线 | 欧美99久久精品乱码影视 | 日韩一区二区三区在线 | 国产精品视频一二三区 | 久草综合在线视频 | 国产成人一区二区 | 国产乱肥老妇国产一区二 | 久久久国产一区二区三区 | 午夜伊人 | 在线天堂免费中文字幕视频 | 亚州春色 | 日韩精品在线看 | 国产网站在线播放 | 最新午夜综合福利视频 | 国产精品亚洲精品 | 欧美不卡一区二区三区 | 在线观看视频一区 | 日本精品久久久久久久 |