<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { list: [1, 2], }; observe(data); const updateComponent = () => { console.log(data.list); }; new Watcher(updateComponent); list[0] = 3;
list[0]
會觸發 的重新執行嗎?updateComponent
可以先思考一下。
答案是否定的,陣列我們只能通過重寫的 、 等方法去觸發更新,詳見push
splice
Vue2響應式系統之陣列 。
如果我們想要替換陣列某個元素的話可以轉一下彎,通過 去實現。splice
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { list: [1, 2], }; observe(data); const updateComponent = () => { console.log(data.list); }; new Watcher(updateComponent); // list[0] = 3; data.list.splice(0, 1, 3);
每次這樣寫太麻煩了,我們可以提供一個 方法供使用者使用。set
/** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */ export function set(target, key, val) { if (Array.isArray(target)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val; } // targe 是物件的情況 // ... }
然後我們直接使用 方法就可以了。set
import { observe, set } from "./reactive"; import Watcher from "./watcher"; const data = { list: [1, 2], }; observe(data); const updateComponent = () => { console.log(data.list); }; new Watcher(updateComponent); // list[0] = 3; // data.list.splice(0, 1, 3); set(data.list, 0, 3);
同陣列 ,我們順便提供一個 的方法,支援陣列響應式的刪除某個元素。set
del
/** * Delete a property and trigger change if necessary. */ export function del(target, key) { if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1); return; } // targe 是物件的情況 // ... }
import { observe, set, del } from "./reactive"; import Watcher from "./watcher"; const data = { obj: { a: 1, b: 2, }, }; observe(data); const updateComponent = () => { const c = data.obj.c ? data.obj.c : 0; console.log(data.obj.a + data.obj.b + c); }; new Watcher(updateComponent); data.obj.c = 3;
updateComponent
方法中雖然使用了 的 屬性,但是在呼叫 之前, 中並沒有 屬性,所以 屬性不是響應式的。obj
c
observe
data.obj
c
c
當我們修改 的值的時候,並不會觸發 的執行。data.obj.c
updateComponent
如果想要變成響應式的話,一種方法就是在最開始就定義 屬性。c
const data = { obj: { a: 1, b: 2, c: null, }, }; observe(data); const updateComponent = () => { const c = data.obj.c ? data.obj.c : 0; console.log(data.obj.a + data.obj.b + c); }; new Watcher(updateComponent); data.obj.c = 3;
另一種方法就是通過 去設定新的屬性了,在 中我們可以將新新增的屬性設定為響應式的。set
set
/** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */ export function set(target, key, val) { if (Array.isArray(target)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val; } // targe 是物件的情況 // key 在 target 中已經存在 if (key in target && !(key in Object.prototype)) { target[key] = val; return val; } const ob = target.__ob__; // target 不是響應式資料 if (!ob) { target[key] = val; return val; } // 將當前 key 變為響應式的 defineReactive(target, key, val); return val; }
回到我們之前的程式:
import { observe, set, del } from "./reactive"; import Watcher from "./watcher"; const data = { obj: { a: 1, b: 2, }, }; observe(data); const updateComponent = () => { const c = data.obj.c ? data.obj.c : 0; console.log(data.obj.a + data.obj.b + c); }; const ob = new Watcher(updateComponent); set(data.obj, "c", 3);
雖然通過 增加了屬性,但是此時 並不會重新觸發,原因的話我們看下依賴圖。set
Watcher
雖然屬性 擁有了 物件,但由於沒有呼叫過依賴屬性 的 ,所以它並沒有收集到依賴。c
Dep
c
Watcher
當然我們可以 完手動呼叫一次相應的 。set
Watcher
const data = { obj: { a: 1, b: 2, }, }; observe(data); const updateComponent = () => { const c = data.obj.c ? data.obj.c : 0; console.log(data.obj.a + data.obj.b + c); }; const ob = new Watcher(updateComponent); set(data.obj, "c", 3); ob.update(); // 手動呼叫 Watcher data.obj.c = 4;
這樣的話,當執行 的時候就會觸發 的執行了。data.obj.c = 4
Watcher
那麼我們能將觸發相應的 的邏輯放到 函數中嗎?Watcher
set
可以看到 裡也有個 ,這個其實當時是為陣列準備的,參考 obj
Dep
Vue2響應式系統之陣列,但 的 什麼都沒收集。obj
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) { /******新位置 *************************/ childOb.dep.depend(); /**********************************/ if (Array.isArray(value)) { // childOb.dep.depend(); //原來的位置 dependArray(value); } } } return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); }, }); } function dependArray(value) { for (let e, i = 0, l = value.length; i < l; i++) { e = value[i]; /******新位置 *************************/ e && e.__ob__ && e.__ob__.dep.depend(); /**********************************/ if (Array.isArray(e)) { // e && e.__ob__ && e.__ob__.dep.depend(); // 原位置 dependArray(e); } } }
因為讀取 屬性,一定先會讀取 屬性,即 。 也同理。a
obj
data.obj.a
b
所以通過上邊的修改, 的 會收集到它的所有屬性的依賴,也就是這裡的 、 的依賴,但因為 和 的依賴是相同的,所以收集到一個依賴。obj
dep
a
b
a
b
但其實我們並不知道 被哪些 依賴,我們只知道和 同屬於一個物件的 和 被哪些 依賴,但大概率 也會被其中的 依賴。c
Watcher
c
a
b
Watcher
c
Watcher
所以我們可以在 中手動執行一下 的 ,依賴 的 大概率會被執行,相應的 也會成功收集到依賴。set
obj
Dep
c
Watcher
c
export function set(target, key, val) { if (Array.isArray(target)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val; } // targe 是物件的情況 // key 在 target 中已經存在 if (key in target && !(key in Object.prototype)) { target[key] = val; return val; } const ob = target.__ob__; // target 不是響應式資料 if (!ob) { target[key] = val; return val; } defineReactive(target, key, val); /******新增 *************************/ ob.dep.notify() /************************************/ return val; }
回到最開始的程式碼:
const data = { obj: { a: 1, b: 2, }, }; observe(data); const updateComponent = () => { const c = data.obj.c ? data.obj.c : 0; console.log(data.obj.a + data.obj.b + c); }; const ob = new Watcher(updateComponent); set(data.obj, "c", 3);
執行完後 除了變為響應式的,也成功觸發了 執行,並且收集到了 。c
Watcher
Watcher
此時如果修改 的值,也會成功觸發 的執行了。c
Watcher
有了上邊的瞭解,刪除就很好解決了。
如果要刪除 屬性,刪除後執行下它相應的 就可以。但 的 是存在閉包中的,我們並不能拿到。a
Dep
a
Dep
退而求其次,我們可以去執行 屬性所在的物件 的 就可以了。a
obj
Dep
/** * Delete a property and trigger change if necessary. */ export function del(target, key) { if (Array.isArray(target)) { target.splice(key, 1); return; } // targe 是物件的情況 const ob = target.__ob__; if (!hasOwn(target, key)) { return; } delete target[key]; if (!ob) { return; } ob.dep.notify(); }
通過為物件收集依賴,將物件、陣列的修改、刪除也變成響應式的了,同時為使用者提供了 和 方法。
到此這篇關於Vue2響應式系統之set和delete的文章就介紹到這了,更多相關Vue2 set和delete內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45