Vue2剝絲抽繭-響應式系統 系列

大家好,我是若川。持續組織了8個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列。另外:目前建有江西|湖南|湖北?籍 前端群,可加我微信進群。


如果打算學vue2源碼,可以看看這個系列https://vue.windliang.wang

目前工作中大概有 40% 的需求是在用 Vue2 的技術棧,所謂知其然更要知其所以然,為了更好的使用 Vue 、更快的排查問題,最近學習了源碼相關的一些知識,雖然網上總結 Vue 的很多很多了,不少自己一個,但也不多自己一個,歡迎一起討論學習,發現問題歡迎指出。

響應式系統要干什么

回到最簡單的代碼:

data?=?{text:?'hello,?world'
}const?updateComponent?=?()?=>?{console.log('收到',?data.text);
}updateComponent()data.text?=?'hello,?liang'
//?運行結果
//?收到?hello,?world

響應式系統要做的事情:某個依賴了 data 數據的函數,當所依賴的 data 數據改變的時候,該函數要重新執行。

我們期望的效果:當上邊 data.text 修改的時候, updateComponent 函數再執行一次。

為了實現響應式系統,我們需要做兩件事情:

  1. 知道 data 中的數據被哪些函數依賴

  2. data 中的數據改變的時候去調用依賴它的函數們

為了實現第 1 點,我們需要在執行函數的時候,將當前函數保存起來,然后在讀取數據的時候將該函數保存到當前數據中。

2 點就迎刃而解了,當修改數據的時候將保存起來的函數執行一次即可。

讀取數據修改數據的時候需要做額外的事情,我們可以通過 Object.defineProperty() ?重寫對象屬性的 getset 函數。

響應式數據

我們來寫一個函數,重寫屬性的 getset 函數。

/***?Define?a?reactive?property?on?an?Object.*/
export?function?defineReactive(obj,?key,?val)?{const?property?=?Object.getOwnPropertyDescriptor(obj,?key);//?讀取用戶可能自己定義了的?get、setconst?getter?=?property?&&?property.get;const?setter?=?property?&&?property.set;//?val?沒有傳進來話進行手動賦值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.這里需要去保存當前在執行的函數/*********************************************/return?value;},set:?function?reactiveSetter(newVal)?{const?value?=?getter???getter.call(obj)?:?val;if?(setter)?{setter.call(obj,?newVal);}?else?{val?=?newVal;}/*********************************************///?2.將依賴當前數據依賴的函數執行/*********************************************/},});
}

為了調用更方便,我們把第 1 步和第 2 步的操作封裝一個 Dep ?類。

export?default?class?Dep?{static?target;?//當前在執行的函數subs;?//?依賴的函數constructor()?{this.subs?=?[];?//?保存所有需要執行的函數}addSub(sub)?{this.subs.push(sub);}depend()?{//?觸發?get?的時候走到這里if?(Dep.target)?{//?委托給?Dep.target?去調用?addSubDep.target.addDep(this);}}notify()?{for?(let?i?=?0,?l?=?this.subs.length;?i?<?l;?i++)?{this.subs[i].update();}}
}Dep.target?=?null;?//?靜態變量,全局唯一

我們將當前執行的函數保存到 Dep 類的 target 變量上。

保存當前正在執行的函數

為了保存當前的函數,我們還需要寫一個 Watcher 類,將需要執行的函數傳入,保存到 Watcher 類中的 getter 屬性中,然后交由 Watcher 類負責執行。

這樣在 Dep 類中, subs 中保存的就不是當前函數了,而是持有當前函數的 Watcher 對象。

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;?//?保存包裝了當前正在執行的函數的?Watcherlet?value;try?{//?調用當前傳進來的函數,觸發對象屬性的?getvalue?=?this.getter.call();}?catch?(e)?{throw?e;}return?value;}/***?Add?a?dependency?to?this?directive.*/addDep(dep)?{//?觸發?get?后會走到這里,收集當前依賴//?當前正在執行的函數的?Watcher?保存到?dep?中的?subs?中dep.addSub(this);}/***?Subscriber?interface.*?Will?be?called?when?a?dependency?changes.*///?修改對象屬性值的時候觸發?set,走到這里update()?{this.run();}/***?Scheduler?job?interface.*?Will?be?called?by?the?scheduler.*/run()?{this.get();}
}

Watcher 的作用就是將正在執行的函數通過 Watcher 包裝后保存到 Dep.target 中,然后調用傳進來的函數,此時觸發對象屬性的 get 函數,會收集當前 Watcher

如果未來修改對象屬性的值,會觸發對象屬性的 set ,接著就會調用之前收集到的 Watcher 對象,通過 Watcher 對象的 uptate 方法,來調用最初執行的函數。

響應式數據

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

import?Dep?from?"./dep";
/***?Define?a?reactive?property?on?an?Object.*/
export?function?defineReactive(obj,?key,?val)?{const?property?=?Object.getOwnPropertyDescriptor(obj,?key);//?讀取用戶可能自己定義了的?get、setconst?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/*********************************************/Object.defineProperty(obj,?key,?{enumerable:?true,configurable:?true,get:?function?reactiveGetter()?{const?value?=?getter???getter.call(obj)?:?val;/*********************************************///?1.這里需要去保存當前在執行的函數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.將依賴當前數據依賴的函數執行dep.notify();/*********************************************/},});
}

Observer 對象

我們再寫一個 Observer 方法,把對象的全部屬性都變成響應式的。

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

我們提供一個 observe 方法來負責創建 Observer 對象。

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

測試

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

import?{?observe?}?from?"./reactive";
import?Watcher?from?"./watcher";
const?data?=?{text:?"hello,?world",
};
//?將數據變成響應式的
observe(data);const?updateComponent?=?()?=>?{console.log("收到",?data.text);
};//?當前函數由?Watcher?進行執行
new?Watcher(updateComponent);data.text?=?"hello,?liang";

此時就會輸出兩次了~

收到?hello,?world
收到?hello,?liang

說明我們的響應式系統成功了。

1c96b3f9599f44865467db55569dea4a.png
image-20220329092722630

先從整體理解了響應式系統的整個流程:

每個屬性有一個 subs 數組,Watcher 會持有當前執行的函數,當讀取屬性的時候觸發 get ,將當前 Watcher 保存到 subs 數組中,當屬性值修改的時候,再通過 subs 數組中的 Watcher 對象執行之前保存的函數。

當然還有億點點細節需要完善,后邊的文章會繼續。vue2源碼系列文章,作者現在寫了12篇了。?https://vue.windliang.wang

cd36d00c3f39fe0e5c02d3594a7e4cbe.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經堅持寫了8年,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

b21578f3a4a93fe444e03915833c52d3.png

掃碼加我微信 ruochuan02、拉你進源碼共讀

今日話題

目前建有江西|湖南|湖北?籍 前端群,想進群的可以加我微信 ruochuan12?進群。分享、收藏、點贊、在看我的文章就是對我最大的支持~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274734.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274734.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274734.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

word文本樣式代碼樣式_使用文本樣式表達創建真相來源

word文本樣式代碼樣式As of After Effects 17.0, you can use expressions to edit text styles in After Effects. Here’s why this would transform your workflow:從After Effects 17.0開始&#xff0c;您可以使用表達式在After Effects中編輯文本樣式。 這就是這將改變您的…

mvn備忘

創建web工程 mvn archetype:generate -DgroupIdcom.malangmedia -DartifactIdautoDeployToJetty -DarchetypeArtifactIdmaven-archetype-webapp -Dversion1.0 添加jetty插件 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.or…

前端框架源碼解讀之Vite

前端工具鏈十年盤點&#xff1a;https://mp.weixin.qq.com/s/FBxVpcdVobgJ9rGxRC2zfgWebpack、Rollup 、Esbuild、Vite ?webpack: 基于 JavaScript 開發的前端打包構建框架&#xff0c;通過依賴收集&#xff0c;模塊解析&#xff0c;生成 chunk&#xff0c;最終輸出生成的打包…

hp-ux_UX中的格式塔-或-為什么設計師如此討厭間距

hp-uxI’ve been lucky so far in my design career to have worked with engineers that seem genuinely interested in learning about design. Perhaps, as mentioned in the title, it’s more about them trying to figure out why it matters so much to us that there i…

很多人都不知道,其實博客園給我們博客開了二級域名

如題。一直都在郵件簽名里寫自己的博客地址為&#xff1a; http://www.cnblogs.com/datacool&#xff1b;直到有天突然發現使用&#xff1a;http://datacool.cnblogs.com也可以訪問。不知道的趕緊測試&#xff0c;后者明顯要酷很多啊。該不是我是最后一個知道的吧&#xff0c;知…

JavaScript 數組新增 4 個非破壞性方法!

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

自行車改裝電動車怎么樣_電動車聽起來應該是什么樣?

自行車改裝電動車怎么樣The sound of an all-electric car accelerating doesn’t have to sound like a standard combustion engine, It could sound like anything.全電動汽車加速的聲音不必聽起來像是標準的內燃機&#xff0c;它可以聽起來像任何東西。 These were the wor…

C++中的三種繼承public,protected,private(轉)

三種訪問權限 public:可以被任意實體訪問 protected:只允許子類及本類的成員函數訪問 private:只允許本類的成員函數訪問 三種繼承方式 public 繼承 protect 繼承 private 繼承 組合結果 基類中 繼承方式 子類中 public &#xff06; public繼承 > public public &#xff0…

如何碎片化時間學前端,了解前沿趨勢

我很開心在前端行業認識了一批優秀且樂于分享的朋友&#xff0c;他們的技術分享與職業觀點讓我獲益良多&#xff0c;推薦給大家一起關注。程序員成長指北Node.js 前端工程化 低代碼考拉小姐姐&#xff0c;一個有趣且樂于分享的人&#xff01;目前就職于某知名外企&#xff0c;負…

谷歌pay破解_Google Pay缺少Google聞名的一件事-UX案例研究

谷歌pay破解Disclaimer: The views expressed in the blog post is purely based on personal experience. It was not influenced by any external factor.When Google launched Tez (now Google Pay) in India during 2017, their primary goal was to design a simple payme…

進階高級前端,這位大前端架構師一定不能錯過

今天給大家介紹一位好朋友&#xff1a;這波能反殺&#xff1a;一位擁有十年工作經驗&#xff0c;對學習方法有獨到理解的資深大前端架構師。一、博客早在 2017 年初&#xff0c;波神在簡書平臺以《前端基礎進階》為名&#xff0c;更新了一系列優質文章&#xff0c;獲得大量認可…

memcached應用策略(轉)

memcached應用策略&#xff08;轉&#xff09;(2012-04-05 11:10:02) 轉載▼標簽&#xff1a; memcached 應用策略 it分類&#xff1a; linux_c memcached應用策略memcached 主要的作用是為減輕大訪問量對數據庫的沖擊&#xff0c;所以一般的邏輯是首先從memcached中讀取數據&a…

突然討厭做前端,討厭代碼_為什么用戶討厭重新設計

突然討厭做前端,討厭代碼重點 (Top highlight)The core of design thinking is to only design something that will bring value and fill the gap in consumer needs. Right? Why else would one design something that no one asked for? While that may be true to some …

那些年我面過的「六年經驗」的初級工程師

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

sql反模式分析2

第八章 多列屬性目標&#xff1a;存儲多值屬性 為一個bug設置多個標簽反模式&#xff1a;創建多個列&#xff0c;為bugs創建tag1&#xff0c;tag2&#xff0c;tag3幾個列保存標簽。標簽必須放于其中一個。1.查詢數據&#xff0c;比如搜索這三列&#xff0c;可以使用in語句2.添…

更多信息請關注微信公眾號_為什么我們更多地關注表面異常?

更多信息請關注微信公眾號Don’t you feel lucky to find a single seasoned curly fry in your bunch of plain old boring french fries? Do you remember highlighting important texts of your study materials before the exams? Both situations might seem irrelevant…

eclipse中的漢字極小的解決方案(轉載)

eclipse中的漢字極小的解決方案(轉載) 可能新裝了eclipse后&#xff0c;寫java代碼的時候發現&#xff0c;寫注釋的時候發現&#xff0c;漢字小的可憐&#xff0c;網上搜一下&#xff0c;又是改字體又是設置字體大小&#xff0c;試用后發現都不是針對這個的方法。 無奈在自己摸…

面試官經常問的觀察者模式如何實現~

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

旅行者 問題_門檻項目:沒有旅行者回到他的原籍城市。

旅行者 問題Sohini Mukherjee| MFA| Spring 2020Sohini Mukherjee | 外交部| 2020年Spring Artivive app to see the full Artivive應用程序可查看完整的#AR experience.#AR體驗。 Prompt:提示&#xff1a; As second semester, first year graduate students, you are at a …

產品經理懂技術=流氓會武術(zz)

最近七年&#xff0c;我都在做互聯網產品&#xff0c;其中前五年分別在創業公司和上市公司里&#xff0c;做別人的產品&#xff1b;近兩年在創業&#xff0c;做自己的產品。 我的體會是&#xff1a;產品經理需要懂技術&#xff0c;創業者尤其需要。但前提是你總覺得有股憋不住的…