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

Vue2剝絲抽繭-響應(yīng)式系統(tǒng)

開發(fā) 前端
每個(gè)屬性有一個(gè) subs 數(shù)組,Watcher 會(huì)持有當(dāng)前執(zhí)行的函數(shù),當(dāng)讀取屬性的時(shí)候觸發(fā) get ,將當(dāng)前 Watcher 保存到 subs 數(shù)組中,當(dāng)屬性值修改的時(shí)候,再通過 subs 數(shù)組中的 Watcher 對(duì)象執(zhí)行之前保存的函數(shù)。

目前工作中大概有 40% 的需求是在用 Vue2 的技術(shù)棧,所謂知其然更要知其所以然,為了更好的使用 Vue 、更快的排查問題,最近學(xué)習(xí)了源碼相關(guān)的一些知識(shí),雖然網(wǎng)上總結(jié) Vue 的很多很多了,不少自己一個(gè),但也不多自己一個(gè),歡迎一起討論學(xué)習(xí),發(fā)現(xiàn)問題歡迎指出。

響應(yīng)式系統(tǒng)要干什么

回到最簡單的代碼:

data = {
text: 'hello, world'
}

const updateComponent = () => {
console.log('收到', data.text);
}

updateComponent()

data.text = 'hello, liang'
// 運(yùn)行結(jié)果
// 收到 hello, world

響應(yīng)式系統(tǒng)要做的事情:某個(gè)依賴了 data 數(shù)據(jù)的函數(shù),當(dāng)所依賴的 data 數(shù)據(jù)改變的時(shí)候,該函數(shù)要重新執(zhí)行。

我們期望的效果:當(dāng)上邊 data.text 修改的時(shí)候, updateComponent 函數(shù)再執(zhí)行一次。

  • 為了實(shí)現(xiàn)響應(yīng)式系統(tǒng),我們需要做兩件事情:
  • 知道 data 中的數(shù)據(jù)被哪些函數(shù)依賴

data 中的數(shù)據(jù)改變的時(shí)候去調(diào)用依賴它的函數(shù)們

為了實(shí)現(xiàn)第 1 點(diǎn),我們需要在執(zhí)行函數(shù)的時(shí)候,將當(dāng)前函數(shù)保存起來,然后在讀取數(shù)據(jù)的時(shí)候?qū)⒃摵瘮?shù)保存到當(dāng)前數(shù)據(jù)中。

第 2 點(diǎn)就迎刃而解了,當(dāng)修改數(shù)據(jù)的時(shí)候?qū)⒈4嫫饋淼暮瘮?shù)執(zhí)行一次即可。

在讀取數(shù)據(jù)和修改數(shù)據(jù)的時(shí)候需要做額外的事情,我們可以通過 Object.defineProperty() 重寫對(duì)象屬性的 get 和 set 函數(shù)。

響應(yīng)式數(shù)據(jù)

我們來寫一個(gè)函數(shù),重寫屬性的 get 和 set 函數(shù)。

/**
* Define a reactive property on an Object.
*/
export function defineReactive(obj, key, val) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 讀取用戶可能自己定義了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 沒有傳進(jìn)來話進(jìn)行手動(dòng)賦值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
/*********************************************/
// 1.這里需要去保存當(dāng)前在執(zhí)行的函數(shù)
/*********************************************/
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;

if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
/*********************************************/
// 2.將依賴當(dāng)前數(shù)據(jù)依賴的函數(shù)執(zhí)行
/*********************************************/
},
});
}

為了調(diào)用更方便,我們把第 1 步和第 2 步的操作封裝一個(gè) Dep 類。

export default class Dep {
static target; //當(dāng)前在執(zhí)行的函數(shù)
subs; // 依賴的函數(shù)
constructor() {
this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
}

addSub(sub) {
this.subs.push(sub);
}

depend() {
// 觸發(fā) get 的時(shí)候走到這里
if (Dep.target) {
// 委托給 Dep.target 去調(diào)用 addSub
Dep.target.addDep(this);
}
}

notify() {
for (let i = 0, l = this.subs.length; i < l; i++) {
this.subs[i].update();
}
}
}

Dep.target = null; // 靜態(tài)變量,全局唯一

我們將當(dāng)前執(zhí)行的函數(shù)保存到 Dep 類的 target 變量上。

保存當(dāng)前正在執(zhí)行的函數(shù)

為了保存當(dāng)前的函數(shù),我們還需要寫一個(gè) Watcher 類,將需要執(zhí)行的函數(shù)傳入,保存到 Watcher 類中的 getter 屬性中,然后交由 Watcher 類負(fù)責(zé)執(zhí)行。

這樣在 Dep 類中, subs 中保存的就不是當(dāng)前函數(shù)了,而是持有當(dāng)前函數(shù)的 Watcher 對(duì)象。

import Dep from "./dep";
export default class Watcher {
constructor(Fn) {
this.getter = Fn;
this.get();
}

/**
* Evaluate the getter, and re-collect dependencies.
*/
get() {
Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
let value;
try {
// 調(diào)用當(dāng)前傳進(jìn)來的函數(shù),觸發(fā)對(duì)象屬性的 get
value = this.getter.call();
} catch (e) {
throw e;
}
return value;
}

/**
* Add a dependency to this directive.
*/
addDep(dep) {
// 觸發(fā) get 后會(huì)走到這里,收集當(dāng)前依賴
// 當(dāng)前正在執(zhí)行的函數(shù)的 Watcher 保存到 dep 中的 subs 中
dep.addSub(this);
}

/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
// 修改對(duì)象屬性值的時(shí)候觸發(fā) set,走到這里
update() {
this.run();
}

/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
run() {
this.get();
}
}

Watcher 的作用就是將正在執(zhí)行的函數(shù)通過 Watcher 包裝后保存到 Dep.target 中,然后調(diào)用傳進(jìn)來的函數(shù),此時(shí)觸發(fā)對(duì)象屬性的 get 函數(shù),會(huì)收集當(dāng)前 Watcher 。

如果未來修改對(duì)象屬性的值,會(huì)觸發(fā)對(duì)象屬性的 set ,接著就會(huì)調(diào)用之前收集到的 Watcher 對(duì)象,通過 Watcher 對(duì)象的 uptate 方法,來調(diào)用最初執(zhí)行的函數(shù)。

響應(yīng)式數(shù)據(jù)

回到我們之前沒寫完的 defineReactive 函數(shù),按照上邊的思路,我們來補(bǔ)全一下。

import Dep from "./dep";
/**
* Define a reactive property on an Object.
*/
export function defineReactive(obj, key, val) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 讀取用戶可能自己定義了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 沒有傳進(jìn)來話進(jìn)行手動(dòng)賦值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}

/*********************************************/
const dep = new Dep(); // 持有一個(gè) Dep 對(duì)象,用來保存所有依賴于該變量的 Watcher
/*********************************************/

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
/*********************************************/
// 1.這里需要去保存當(dāng)前在執(zhí)行的函數(shù)
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;
}
/*********************************************/
// 2.將依賴當(dāng)前數(shù)據(jù)依賴的函數(shù)執(zhí)行
dep.notify();
/*********************************************/
},
});
}

Observer 對(duì)象

我們?cè)賹懸粋€(gè) Observer 方法,把對(duì)象的全部屬性都變成響應(yīng)式的。


export class Observer {
constructor(value) {
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]);
}
}
}

我們提供一個(gè) observe 方法來負(fù)責(zé)創(chuàng)建 Observer 對(duì)象。

export function observe(value) {
let ob = new Observer(value);
return ob;
}

測(cè)試

將上邊的方法引入到文章最開頭的例子,來執(zhí)行一下:

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello, world",
};
// 將數(shù)據(jù)變成響應(yīng)式的
observe(data);

const updateComponent = () => {
console.log("收到", data.text);
};

// 當(dāng)前函數(shù)由 Watcher 進(jìn)行執(zhí)行
new Watcher(updateComponent);

data.text = "hello, liang";

此時(shí)就會(huì)輸出兩次了~

收到 hello, world
收到 hello, liang

說明我們的響應(yīng)式系統(tǒng)成功了。

總結(jié)

先從整體理解了響應(yīng)式系統(tǒng)的整個(gè)流程:

每個(gè)屬性有一個(gè) subs 數(shù)組,Watcher 會(huì)持有當(dāng)前執(zhí)行的函數(shù),當(dāng)讀取屬性的時(shí)候觸發(fā) get ,將當(dāng)前 Watcher 保存到 subs 數(shù)組中,當(dāng)屬性值修改的時(shí)候,再通過 subs 數(shù)組中的 Watcher 對(duì)象執(zhí)行之前保存的函數(shù)。

責(zé)任編輯:武曉燕 來源: windliang
相關(guān)推薦

2022-04-03 19:27:35

Vue2響應(yīng)式系統(tǒng)

2022-04-06 07:28:47

數(shù)組響應(yīng)式系統(tǒng)

2022-04-02 09:56:41

Vue2響應(yīng)式系統(tǒng)

2022-04-14 08:46:46

響應(yīng)式系統(tǒng)js

2022-03-31 10:15:10

分支切換響應(yīng)式系統(tǒng)

2022-04-12 10:05:18

響應(yīng)式系統(tǒng)異步隊(duì)列

2022-04-10 11:04:40

響應(yīng)式系統(tǒng)setdelete

2022-08-31 08:09:35

Vue2AST模版

2024-09-02 16:10:19

vue2前端

2024-03-07 12:54:06

數(shù)據(jù)分析師企業(yè)

2023-03-02 11:51:00

數(shù)據(jù)分析師企業(yè)

2019-04-25 14:20:56

數(shù)據(jù)分析套路工具

2021-05-19 14:25:19

前端開發(fā)技術(shù)

2022-06-26 00:00:02

Vue3響應(yīng)式系統(tǒng)

2024-03-15 11:47:19

Vue2前端權(quán)限控制

2023-11-19 18:53:27

Vue2MVVM

2016-10-19 20:47:55

vuevue-cli移動(dòng)端

2020-09-25 07:40:39

技術(shù)開發(fā)選型

2020-06-09 11:35:30

Vue 3響應(yīng)式前端

2019-07-01 13:34:22

vue系統(tǒng)數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 91一区| 亚洲字幕在线观看 | 在线日韩精品视频 | 午夜欧美 | 日韩一区和二区 | 成人在线网址 | 免费一区| 国产精品视频久久久久久 | 欧美jizzhd精品欧美巨大免费 | 欧美在线激情 | 国产精品69毛片高清亚洲 | 大学生a级毛片免费视频 | 免费国产精品久久久久久 | 欧美精品久久久久久久久老牛影院 | 久久91av | 97久久精品 | 欧美视频网 | 天天碰日日操 | 在线中文字幕第一页 | 91久久精品国产91久久 | 午夜免费视频 | 黄色综合| 日日爱夜夜操 | 欧美日韩最新 | 中文字幕一区二区三区四区五区 | 色噜噜狠狠色综合中国 | 少妇一级淫片免费播放 | 麻豆精品国产91久久久久久 | 国产一二三区精品视频 | 91精品国产91久久综合桃花 | 草比网站 | 日韩精品一区二区三区免费视频 | 欧美一区不卡 | 国产69久久精品成人看动漫 | 亚洲精品日韩在线 | 久久久久久久一区 | 国产三区在线观看视频 | 一区二区在线不卡 | 亚洲精品精品 | 99re热精品视频国产免费 | 日本不卡一区二区三区在线观看 |