Vue2 響應式系統設計原理與實現

文章目錄

  • Vue2 響應式系統設計原理與實現

Vue2 響應式系統設計原理與實現

Vue2 的響應式原理主要基于以下幾點:

使用 Object.defineProperty () 方法對數據對象的屬性進行劫持
當數據發生變化時,通知依賴該數據的視圖進行更新
實現一個發布 - 訂閱模式,包含 Watcher(訂閱者)、Dep(依賴收集器)等核心概念

創建以下幾個核心部分:

Observer:遞歸地將數據對象的所有屬性轉換為響應式
Dep:依賴收集器,負責收集和通知訂閱者
Watcher:訂閱者,當數據變化時執行相應的回調函數
下面我們來簡單實現以下

// 依賴收集器類:管理某個數據的所有依賴(訂閱者)
class Dep {// 構造函數初始化constructor() {// 存儲所有訂閱者的數組this.subscribers = [];}// 添加訂閱者到收集器addSub(sub) {// 檢查訂閱者是否存在且有update方法if (sub && sub.update) {this.subscribers.push(sub);}}// 通知所有訂閱者數據已更新notify() {// 遍歷所有訂閱者并調用其update方法this.subscribers.forEach(sub => {sub.update();});}
}// 訂閱者類:代表一個依賴,數據變化時執行更新操作
class Watcher {// 構造函數:接收Vue實例、屬性名和回調函數constructor(vm, key, callback) {this.vm = vm;         // 存儲Vue實例的引用this.key = key;       // 要監視的數據屬性名this.callback = callback; // 數據變化時要執行的回調函數this.value = this.get();  // 初始化時獲取值,觸發getter完成依賴收集}// 獲取數據并將當前訂閱者添加到依賴收集器get() {// 將當前訂閱者設為Dep的目標,標記為當前需要收集的依賴Dep.target = this;// 訪問數據屬性,觸發其getter,從而完成依賴收集const value = this.vm[this.key];// 重置Dep.target,避免后續操作錯誤收集依賴Dep.target = null;// 返回獲取到的值return value;}// 數據變化時執行的更新方法update() {// 獲取新值const newValue = this.get();// 保存舊值const oldValue = this.value;// 只有當新舊值不同時才執行回調if (newValue !== oldValue) {// 更新當前值為新值this.value = newValue;// 調用回調函數,并將Vue實例作為上下文,傳入新值和舊值this.callback.call(this.vm, newValue, oldValue);}}
}// 將普通對象轉換為響應式對象的函數
function observe(data) {// 如果數據不是對象或為null,則無需處理if (!data || typeof data !== 'object') {return;}// 創建觀察者實例處理數據return new Observer(data);
}// 觀察者類:負責將對象的所有屬性轉換為響應式
class Observer {// 構造函數:接收需要處理的數據對象constructor(data) {this.data = data;// 遍歷對象屬性并處理this.walk(data);}// 遍歷對象的所有屬性walk(data) {// 獲取對象所有自有屬性的鍵名Object.keys(data).forEach(key => {// 為每個屬性定義響應式this.defineReactive(data, key, data[key]);});}// 核心方法:使用Object.defineProperty定義響應式屬性defineReactive(obj, key, val) {// 為當前屬性創建一個依賴收集器const dep = new Dep();// 如果屬性值是對象,遞歸處理使其也成為響應式observe(val);// 使用Object.defineProperty劫持屬性的getter和setterObject.defineProperty(obj, key, {enumerable: true,   // 允許屬性被枚舉(例如在for...in循環中)configurable: true, // 允許屬性被配置(例如刪除屬性)// 當屬性被訪問時觸發的getterget() {// 如果當前有需要收集的依賴(Dep.target存在)if (Dep.target) {// 將當前依賴添加到收集器中dep.addSub(Dep.target);}// 返回屬性值return val;},// 當屬性被修改時觸發的setterset(newVal) {// 如果新值和舊值相同,則不做處理if (newVal === val) {return;}// 更新屬性值val = newVal;// 如果新值是對象,需要將其轉換為響應式observe(newVal);// 通知所有依賴當前屬性的訂閱者數據已更新dep.notify();}});}
}// 簡化版Vue類:整合響應式系統
class Vue {// 構造函數:接收配置選項constructor(options) {this.$options = options;  // 存儲配置選項this.$data = options.data; // 存儲數據對象// 將數據轉換為響應式observe(this.$data);// 將data中的屬性代理到Vue實例上,方便直接訪問this.proxyData(this.$data);// 如果有created生命周期鉤子,執行它if (options.created) {options.created.call(this);}}// 數據代理方法:使vm.xxx等價于vm.$data.xxxproxyData(data) {// 遍歷data的所有屬性Object.keys(data).forEach(key => {// 在Vue實例上定義與data屬性同名的屬性Object.defineProperty(this, key, {// 當訪問vm.xxx時,返回vm.$data.xxx的值get() {return data[key];},// 當修改vm.xxx時,同步修改vm.$data.xxxset(newVal) {data[key] = newVal;}});});}// 提供$watch方法,用于監視數據變化$watch(key, callback) {// 創建一個新的訂閱者,關聯到指定的屬性和回調new Watcher(this, key, callback);}
}// 使用示例
const vm = new Vue({data: {message: 'Hello Vue',count: 0,user: {name: 'John'}},created() {console.log('初始化完成:', this.message);}
});// 添加監聽器,當message變化時觸發
vm.$watch('message', (newVal, oldVal) => {console.log(`message變化: ${oldVal} -> ${newVal}`);
});// 添加監聽器,當count變化時觸發
vm.$watch('count', (newVal, oldVal) => {console.log(`count變化: ${oldVal} -> ${newVal}`);
});// 添加監聽器,當user.name變化時觸發
vm.$watch('user.name', (newVal, oldVal) => {console.log(`user.name變化: ${oldVal} -> ${newVal}`);
});// 測試數據變化,觀察是否觸發更新
vm.message = 'Hello World'; // 觸發message的更新
vm.count = 1; // 觸發count的更新
vm.user.name = 'Jane'; // 觸發user.name的更新

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

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

相關文章

探索 JUC:Java 并發編程的神奇世界

探索 JUC:Java 并發編程的神奇世界 在 Java 編程領域,隨著多核處理器的普及和應用場景復雜度的提升,并發編程變得愈發重要。Java 并發包(JUC,Java.util.concurrent)就像是一座寶藏庫,為開發者提…

selenium采集數據怎么應對反爬機制?

selenium是一個非常強大的瀏覽器自動化工具,通過操作瀏覽器來抓取動態網頁內容,可以很好的處理JavaScript和AJAX加載的網頁。 它能支持像點擊按鈕、懸停元素、填寫表單等各種自動化操作,所以很適合自動化測試和數據采集。 selenium與各種主流…

指定文件夾上的壓縮圖像格式tiff轉換為 jpg 批量腳本

文章大綱 背景簡介 代碼 背景簡介 隨著數字成像技術在科研、醫學影像和遙感等領域的廣泛應用,多頁TIFF(Tag Image File Format)文件因其支持多維數據存儲和高位深特性,成為存儲序列圖像、顯微鏡切片或衛星遙感數據的首選格式。然而在實際應用中,這類文件存在以下顯著痛點…

Docker 部署 MySQL 8.0 完整指南:從拉取鏡像到配置遠程訪問

目錄前言一、拉取鏡像二、查看鏡像三、運行容器命令參數說明:四、查看運行容器五、進入容器內部六、修改 MySQL 配置1. 創建配置文件2. 配置內容七、重啟 MySQL 服務八、設置 Docker 啟動時自動啟動 MySQL九、再次重啟 MySQL十、授權遠程訪問1. 進入容器內部2. 登錄…

IntelliJ IDEA 常用快捷鍵筆記(Windows)

前言:特別標注的快捷鍵(Windows)快捷鍵功能說明Ctrl Alt M將選中代碼提取成方法Ctrl Alt T包裹選中代碼塊(try/catch、if、for 等)Ctrl H查看類的繼承層次Alt 7打開項目結構面板Ctrl F12打開當前文件結構視圖Ct…

疏老師-python訓練營-Day54Inception網絡及其思考

浙大疏錦行 DAY54 一、 inception網絡介紹 今天我們介紹inception,也就是GoogleNet 傳統計算機視覺的發展史 從上面的鏈接,可以看到其實inceptionnet是在resnet之前的,那為什么我今天才說呢?因為他要引出我們后面的特征融合和…

LeetCode第3304題 - 找出第 K 個字符 I

題目 解答 class Solution {public char kthCharacter(int k) {int n 0;int v 1;while (v < k) {v << 1;n;}String target kthCharacterString(n);return target.charAt(k - 1);}public String kthCharacterString(int n) {if (n 0) {return "a";}Str…

Codeforces Round 1043 (Div. 3) D-F 題解

D. From 1 to Infinity 題意 有一個無限長的序列&#xff0c;是把所有正整數按次序拼接&#xff1a;123456789101112131415...\texttt{123456789101112131415...}123456789101112131415...。求這個序列前 k(k≤1015)k(k\le 10^{15})k(k≤1015) 位的數位和。 思路 二分出第 …

【C語言16天強化訓練】從基礎入門到進階:Day 7

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題、洛谷刷題、C/C基礎知識知識強化補充、C/C干貨分享&學習過程記錄 &#x1f349;學習方向&#xff1a;C/C方向學習者…

【AI基礎:神經網絡】16、神經網絡的生理學根基:從人腦結構到AI架構,揭秘道法自然的智能密碼

“道法自然,久藏玄冥”——人工神經網絡(ANN)的崛起并非偶然,而是對自然界最精妙的智能系統——人腦——的深度模仿與抽象。從單個神經元的信號處理到大腦皮層的層級組織,從突觸可塑性的學習機制到全腦并行計算的高效能效,生物大腦的“玄冥”智慧為AI提供了源源不斷的靈感…

容器安全實踐(一):概念篇 - 從“想當然”到“真相”

在容器化技術日益普及的今天&#xff0c;許多開發者和運維人員都將應用部署在 Docker 或 Kubernetes 中。然而&#xff0c;一個普遍存在的誤解是&#xff1a;“容器是完全隔離的&#xff0c;所以它是安全的。” 如果你也有同樣的想法&#xff0c;那么你需要重新審視容器安全了。…

騰訊開源WeKnora:新一代文檔理解與檢索框架

引言&#xff1a;文檔智能處理的新范式 在數字化時代&#xff0c;企業和個人每天都面臨著海量文檔的處理需求&#xff0c;從產品手冊到學術論文&#xff0c;從合同條款到醫療報告&#xff0c;非結構化文檔的高效處理一直是技術痛點。2025年8月&#xff0c;騰訊正式開源了基于大…

C++之list類的代碼及其邏輯詳解 (中)

接下來我會依照前面所說的一些接口以及list的結構來進行講解。1. list_node的結構1.1 list_node結構體list由于其結構為雙向循環鏈表&#xff0c;所以我們在這里要這么初始化_next&#xff1a;指向鏈表中下一個節點的指針_prev&#xff1a;指向鏈表中上一個節點的指針_val&…

新能源汽車熱管理仿真:蒙特卡洛助力神經網絡訓練

研究背景在新能源汽車的熱管理仿真研究中&#xff0c;神經網絡訓練技術常被應用于系統降階建模。通過這一方法&#xff0c;可以構建出高效準確的代理模型&#xff0c;進而用于控制策略的優化、系統性能的預測與評估&#xff0c;以及實時仿真等任務&#xff0c;有效提升開發效率…

第十九講:C++11第一部分

目錄 1、C11簡介 2、列表初始化 2.1、{}初始化 2.2、initializer_list 2.2.1、成員函數 2.2.2、應用 3、變量類型推導 3.1、auto 3.2、decltype 3.3、nullptr 4、范圍for 5、智能指針 6、STL的一些變化 7、右值引用和移動語義 7.1、右值引用 7.2、右值與左值引…

書寫本體論視域下的文字學理論重構

在符號學與哲學的交叉領域&#xff0c;文字學&#xff08;Grammatologie&#xff09;作為一門顛覆性學科始終處于理論風暴的中心。自德里達1967年發表《論文字學》以來&#xff0c;傳統語言學中"語音中心主義"的霸權地位遭遇根本性動搖&#xff0c;文字不再被視為語言…

為什么要做架構設計?架構設計包含哪些內容?

大家好,我是IT孟德,You can call me Aman(阿瞞,阿彌陀佛的ē,Not阿門的ā),一個喜歡所有對象(熱愛技術)的男人。我正在創作架構專欄,秉承ITer開源精神分享給志同道合(愛江山愛技術更愛美人)的朋友。專欄更新不求速度但求質量(曹大詩人傳世作品必屬精品,請腦補一下《…

Vue2封裝Axios

一、介紹Axios 是一個基于 promise 的 HTTP 庫&#xff0c;簡單的講就是可以發送get、post等請求。二、安裝npm install axios --save二、axios不同請求方式axios(config)這是 Axios 的核心方法&#xff0c;用于發送自定義配置的 HTTP 請求。通過傳入一個包含請求配置的對象&am…

DataAnalytics之Tool:Metabase的簡介、安裝和使用方法、案例應用之詳細攻略

DataAnalytics之Tool&#xff1a;Metabase的簡介、安裝和使用方法、案例應用之詳細攻略 目錄 Metabase的簡介 1、特點 Metabase的安裝和使用方法 1、安裝 快速設置&#xff1a;開發環境 前端快速設置 后端快速設置 2、使用方法 Metabase的案例應用 Metabase的簡介 Met…

frp v0.64.0 更新:開源內網穿透工具,最簡潔教程

frp是一款跨平臺的內網穿透工具&#xff0c;支持 Windows、macOS 與 Linux&#xff0c;它需要你有一臺擁有固定公網 IP 的電腦&#xff0c;VPS 最好&#xff0c;然后就能愉快的進行內網穿透了。還支持 https&#xff0c;甚至可以用它進行小程序開發。Appinn v0.64.0 新增token…