源碼的角度分析Vue2數據雙向綁定原理

什么是雙向綁定

我們先從單向綁定切入,其實單向綁定非常簡單,就是把Model綁定到View,當我們用JavaScript代碼更新Model時,View就會自動更新。那么雙向綁定就可以從此聯想到,即在單向綁定的基礎上,用戶更新了View,Model數據也會自動被更新,這種情況就是雙向綁定。實例如下:

?當用戶填寫表單,View的狀態就被更新了,如果此時可以自動更新Model的狀態,那就相當于我們把Model和View做了雙向綁定關系圖如下:

雙向綁定的原理是什么

我們都知道Vue是數據雙向綁定的框架,雙向綁定由三個重要部分組成

  • Model:應用數據以及業務邏輯
  • View:應用視圖,各類UI組件
  • ViewModel:框架封裝的核心,它負責將數據與視圖關聯起來

上面這個分層的架構方案,即是我們經常耳熟能詳的MVVM,他的控制層的核心功能便是“數據雙向綁定”

理解ViewModel

它的主要職責就是:

  • 數據變化后更新視圖
  • 視圖變化后更細數據

當然,它還有兩個主要部分組成

  • 監聽器:對所有的數據進行監聽
  • 解析器(Compiler):對每個元素節點的指令進行掃描和解析,根據指令模板替換數據,以及綁定相應的更新函數

實現雙向綁定

我們還是以Vue為例,先看看Vue中雙向綁定流程是什么

1.new Vue()首先執行初始化,對data執行響應化處理,這個過程發生在Observe中(類似于Vue生命周期created之前執行的一系列初始化操作)

2.同時對模板執行編譯,找到其中動態綁定的數據,從data中獲取并初始化視圖,這個過程發生在Complie中(類似于Vue生命周期mounted之前執行的一系列初始化操作)

3.同時定義一個更新函數和Watcher,將來對應數據變化時Watcher會調用更新函數

4.由于data的某個key在一個視圖中可能會出現多次,所以每個key都需要一個管家Dep來管理多個Watcher

5.將來data中數據一旦發生變化,會首先找到ui應的Dep,同時所有Watcher執行更新函數

流程圖如下:

劫持監聽所有屬性Observe

先來一個構造函數:執行初始化,對data執行響應化處理

  class Vue {  constructor(options) {  this.$options = options;  this.$data = options.data;  // 對data選項做響應式處理  observe(this.$data);  // 代理data到vm上  proxy(this);  // 執行編譯  new Compile(options.el, this);  }  
}  

對data選項進行響應化具體操作


function proxy(vm) {Object.keys(vm.$data).forEach(key=>{Object.defineProperty(vm, key, {get() {return vm.$data[key]},set(newVal) {vm.$data[key] = newVal}})})
}function observe(obj) {  if (typeof obj !== "object" || obj == null) {  return;  }  new Observer(obj);  
}  class Observer {  constructor(value) {  this.value = value;  this.walk(value);  }  walk(obj) {  Object.keys(obj).forEach((key) => {  defineReactive(obj, key, obj[key]);  });  }  
}  

編譯Complie

對每個元素節點的指令進行掃面和解析,根據指令模板替換數據,同時綁定相應的更新函數

class Compile {  constructor(el, vm) {  this.$vm = vm;  this.$el = document.querySelector(el);  // 獲取dom  if (this.$el) {  this.compile(this.$el);  }  }  compile(el) {  const childNodes = el.childNodes;   Array.from(childNodes).forEach((node) => { // 遍歷子元素  if (this.isElement(node)) {   // 判斷是否為節點  console.log("編譯元素" + node.nodeName);  } else if (this.isInterpolation(node)) { // 判斷是否為插值文本 {{}} console.log("編譯插值?本" + node.textContent);  }  if (node.childNodes && node.childNodes.length > 0) {  // 判斷是否有子元素  this.compile(node);  // 對子元素進行遞歸遍歷  }  });  }  isElement(node) {  return node.nodeType == 1;  }  isInterpolation(node) {  return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);  }  
}  

依賴收集

????????Vue2.x中的響應式原理主要死依賴于Object.defineProperty()方法實現屬性的getter和setter。在Vue中,每個組件實例都有一個對應的Watcher實例,Watcher實例會負責依賴的收集以及觸發更新。

????????具體來說,當一個組件渲染時,會執行render函數來生成Virtual DOM,并且在執行過程中,當訪問到組件的data中的屬性時,會觸發屬性的getter方法。并在getter方法中,會進行依賴收集,將當前的Watcher對象存儲到當前屬性的依賴列表中。

當個屬性收集具體如下圖:

多個屬性的收集如下:

依賴收集的過程可以簡單描述如下:

1.在組件渲染過程中,當訪問data中的屬性時,會觸發屬性的getter方法;

2.在getter方法中,會將當前Watcher對象存儲到當前依賴列表中(Dep);

3.當屬性被修改時,會觸發屬性的setter方法;

4.在setter方法中,會通知所有依賴于該屬性的Watcher對象,執行更新操作;

這樣,當數據發生變化 時,Vue能夠精確的知道哪些地方需要更新,并且只更新相關的部分,提高了性能(因為只有存儲了觸發getter方法時的watcher,做到了對應關系)

簡化版的實現代碼如下:

// 定義 Dep 類,用于管理依賴
class Dep {constructor() {this.subscribers = new Set(); // 存儲 Watcher 實例的集合}// 添加依賴depend() {if (activeWatcher) {this.subscribers.add(activeWatcher);}}// 通知依賴更新notify() {this.subscribers.forEach(watcher => {watcher.update();});}
}let activeWatcher = null;// 定義 Watcher 類,用于觀察數據變化
class Watcher {constructor(update) {this.update = update; // 更新函數this.value = null; // 存儲當前值this.get(); // 初始化時進行依賴收集}// 獲取當前值,并進行依賴收集get() {activeWatcher = this;// 在這里模擬讀取 data 中的屬性的過程this.value = this.update();activeWatcher = null;}
}// 定義 reactive 函數,將對象轉換為響應式對象
function reactive(obj) {// 遍歷對象的每個屬性,轉換為響應式屬性for (let key in obj) {let value = obj[key];const dep = new Dep(); // 每個屬性對應一個依賴管理對象Object.defineProperty(obj, key, {get() {dep.depend(); // 依賴收集return value;},set(newValue) {value = newValue;dep.notify(); // 通知依賴更新}});}return obj;
}// 示例用法
const data = reactive({count: 0
});new Watcher(() => {console.log("Value updated:", data.count);
});data.count++; // 觸發更新

在這個示例中

  • Dep類用于管理依賴,每個響應式屬性都會對應一個'Dep'實例,用于存儲依賴于該屬性的'Watcher'對象
  • ’Watcher‘類用于觀察數據變化,當數據發生改變時會執行更新函數
  • ’reactive‘函數用于將對象轉為響應式對象,在該函數中,通過'Object.defineProperty'來定義對象的屬性,實現了屬性的getter和setter,從而在讀取和修改屬性時進行依賴收集和通知更新

?在實際的 Vue 源碼中,會有更復雜的邏輯和優化,但基本原理與上述代碼類似。

個人備注說明:

1.上述代碼設計中為什么activeWatcher變量是全局存儲,同時在Watcher類的get方法中先是指向了this,然后又賦值為空?

答疑:在Vue源碼中,activeWatcher 通常是通過棧結構來管理的,這里這樣可以支持嵌套的依賴收集。而上述代碼Watcher 類的 get 方法中,將activeWatcher 設置為當前的Watcher實例的原因是依賴收集過程中給需要知道當前的依賴是誰,從而在屬性發生變化時可以通知到相關的 Watcher 實例進行更新。在依賴收集完成后,將activeWatcher 設置為空的原因時為了防止在非依賴收集的情況下,誤操作導致activeWatcher 保留了值。

一般來說,在Vue的相應式系統中,activeWatcher 在以下幾種情況下會被設置為某個具體的 Watcher 對象:

  • 組件渲染過程中:在組件的渲染過程中,Vue會創建一個Watcher對象來實現觀察組件的渲染函數。此時activeWatcher 會被設置為這個渲染Watcher對象,以便在渲染函數中訪問組件的響應式數據時進行依賴收集
  • 計算屬性或者偵聽的求職過程中:當計算屬性或者偵聽器的值被求值時,Vue會創建一個Watcher對象來觀察相關的響應式數據,以便在求值過程中訪問相關的響應式數據時進行依賴收集。
  • 用戶手動創建的Watcher

以上情況下,activeWatcher 都會在相應的Watcher對象的get方法中被設置為當前Watcher實例。在依賴收集完成后,activeWatcher 會被重新設置為null,以便下一次依賴收集的時候再次被設置為新的Watcher對象。

2.Watcher 類中的value的作用是什么?

答疑:在 Vue 的響應式系統中,Watcher 類負責觀察數據的變化,value 的存在可以讓 Watcher 在依賴收集時記錄當前的值,在數據發生變化時,可以通過對比新舊值來判斷是否需要觸發更新操作。

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

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

相關文章

微信開發者工具-代碼管理和碼云Github遠程倉庫集成

目錄 思考:IDE如何進行代碼管理 代碼管理方式 一、自身提供服務 二、Git 擴展 1、環境準備 2、創建項目代碼 3、進行項目Git初始化 4、在碼云新建遠程倉庫 5、將項目進行遠程倉庫關聯 三、SVN擴展 四、代碼管理 思考:IDE如何進行代碼管理 初識開…

服務器部署測試環境回顧與改進建議

任務概述: 原計劃在2小時內完成的任務,由于遇到一系列挑戰,最終耗時1.5天。任務目標是在無外網環境的服務器上建立測試環境,涉及將SSD硬盤數據遷移至服務器、SSH連接、運行測試程序并監控服務器功耗。 高效實施策略:…

fs讀取目錄、文件

fs讀取文件 process.cwd() 是 Node.js 中的一個方法,它返回 Node.js 進程的當前工作目錄。這個工作目錄通常是啟動 Node.js 進程時所在的目錄。 const fs require(fs); const path require(path);// 讀取指定目錄 const configPath path.join(process.cwd(), c…

StarRocks實戰——貝殼找房數倉實踐

目錄 前言 一、StarRocks在貝殼的應用現狀 1.1 歷史的數據分析架構 1.2 OLAP選型 1.2.1 離線場景 1.2.2 實時場景 1.2.3 StarRocks 的引入 二、StarRocks 在貝殼的分析實踐 2.1 指標分析 2.2 實時業務 2.3 可視化分析 三、未來規劃 3.1 StarRocks集群的穩定性 3…

PMP考試培訓費用多少錢?

PMP考試的相關費用包括報名費用、培訓費用和證書續證費用三個部分。 一、PMP考試報名費用: 首次報考費用為3900元,如果未通過考試可以在英文報名有效期內進行補考報名,補考費用為2500元。 付費方式是在項目管理學會官方網站上提交報考資料…

企業數字化轉型的第一步:由被動多云向主動多云轉變

隨著經濟環境、市場形勢、技術發展、用戶需求等諸多因素的變化,數字化轉型為企業進一步提升效率和競爭力、提供更加豐富的個性化產品和服務、進行業務場景創新、探尋新的增長機會和運營模式提供了嶄新的途徑。越來越多的企業意識到,數字化轉型已不是企業…

第1篇 Linux Docker安裝rabbitmq

Docker安裝RabbitMq 1、搜索rabbitmq鏡像 docker search rabbitmq2、下載rabbitmq鏡像 docker pull rabbitmq3、運行rabbitmq服務 docker run -d --name rabbitmq --restart always -p 15672:15672 -p 5672:5672 rabbitmq4、訪問rabbitmq http://192.168.1.x:15672 5、rab…

亞信安慧AntDB:打破數據孤島,實現實時處理

AntDB數據庫以其獨特的創新能力在分布式數據庫領域引領潮流。其中,融合統一與實時處理是其兩大核心創新能力,為其贏得廣泛關注與贊譽。融合統一意味著AntDB能夠將多種不同類型的數據庫融合為一體,實現數據的統一管理與處理,極大地…

電視盒子什么品牌好?資深數碼粉強推口碑電視盒子推薦

我對各類數碼產品是非常熟悉的,尤其是電視盒子,用過超十五款了,涵蓋了各個主流品牌,最近看到很多朋友在討論不知道電視盒子什么品牌好,我這次要來分享的就是口碑最好的五款電視盒子推薦給各位不懂如何選電視盒子的新手…

AI、AIGC、AGI、ChatGPT它們的區別?

今天咱們聊點熱門話題,來點科普時間——AI、AIGC、AGI和ChatGPT到底是啥?這幾個詞聽起來好像挺神秘的,但其實它們就在我們生活中。讓我們一起探索這些術語的奧秘! AI(人工智能):先說說AI&#…

數倉技術選型特點

高性能:用全并行的MPP架構數據庫,業務數據被分散存儲在多個節點上,數據分析任務被推送到數據所在位置就近執行,并行地完成大規模的數據處理工作,實現對數據處理的快速響應。 易擴展:Shared-Nothing開放架構…

電梯物聯網之梯控相機方案-防止電瓶車進電梯

梯控現狀 隨著電梯產品在智能化建筑的日益普及,對于電梯的智能化管理 安全性需求 的要求越來越迫切。尤其今年來隨著電瓶車的大量普及,發起多起樓道、轎廂電瓶車著火惡性事件, 造成了極大的社會 負面影響。控制電瓶車進入單元門,樓道以及電梯…

Vue官網“食用指南”

把Vue官網當做一個工具來用,有問題,先來官網查一查。 官網中常用的板塊 官網:https://cn.vuejs.org/上手后,最常用的模塊是【快速上手】【API】。所以務必要知道這兩個模塊在哪里,怎么使用。![image.png](https://img…

/proc/cpuinfo文件內容詳解

/proc/cpuinfo 文件包含了有關系統 CPU 的信息,每一行代表一個屬性及其對應的值。以下是一些常見的屬性及其含義: 1. processor:表示 CPU 的物理編號,通常從 0 開始遞增。 2. vendor_id:CPU 廠商的名稱,如…

藍橋杯 砝碼稱重 dp/dfs

題目鏈接&#xff1a; https://www.lanqiao.cn/problems/1447/learning/?subject_code1&group_code4&match_num12&match_flow1&origincup 思想&#xff1a;dfs暴力枚舉過一半的分 代碼&#xff1a; #include<bits/stdc.h> using namespace std;#def…

快速開發一個鴻蒙的頁面

文章目錄 前言常用組件快速開啟簡單的鴻蒙頁面總結 一、前言 鴻蒙要想快速上手&#xff0c;那么就需要對基礎的組件使用比較熟悉&#xff0c;這里就羅列開發中常見的基礎組件的使用。 只要是寫android的&#xff0c;對于這些組件的使用還是能很快上手的&#xff0c;只要多多…

01-prometheus監控系統-安裝部署prometheus

一、準備環境 主機名ip配置prometheus-server3110.0.0.311核1g-20GBprometheus-server3210.0.0.311核1g-20GBprometheus-server3310.0.0.311核1g-20GB 二、下載/上傳軟件包 1&#xff0c;軟件包地址 這里給大家準備了百度云盤的安裝包&#xff1b; 鏈接&#xff1a;https:/…

FRM模型十二:極值理論

目錄 極值理論介紹GEVPOT 代碼實現 極值理論介紹 在風險管理中&#xff0c;將事件分為高頻高損、高頻低損、低頻高損、低頻低損。其中低頻高損是一種非常棘手的損失事件&#xff0c;常出現在市場大跌、金融體系崩潰、金融危機以及自然災害等事件中。 由于很難給極端事件一個準…

Spring 學習記錄

Spring 學習記錄 1. Spring和SpringFrameWork1.1 廣義的Spring2.1 狹義的Spring2.3 SpringFrameWork / Spring框架圖 2. Spring IOC容器(即上圖中的Core Container)2.1 相關概念 (IOC DI 容器 組件)2.2 Spring IOC容器的作用2.3 Spring IOC容器接口和具體實現類 3. Spring IOC …

flask 數據庫遷移報錯 Error: No such command ‘db‘.

初學FLASK&#xff0c;使用pycharm的terminal 啟動&#xff0c;實現數據庫遷移 文件結構 項目啟動文件不在一級目錄pycharm>terminal啟動 由于自己初入 python flask 很多東西并不懂&#xff0c;只能依葫蘆畫瓢&#xff0c;使用如下命令,輸入完第一行命令執行沒有任何錯誤…