vue 是一個易上手的框架,許多便捷功能都在其內部做了集成,其中最有區別性的功能就是其潛伏於底層的響應式系統。元件狀態都是響應式的 JavaScript 物件。當更改它們時,視圖會隨即更新,這會讓狀態管理更加簡單直覺。那麼,Vue 響應性系統是如何實現的呢?本文也是在閱讀了 Vue 原始碼後的理解以及模仿實現,所以跟隨作者的思路,我們一起由淺入深的探索一下vue吧! 【相關推薦:vuejs影片教學】
本文 Vue 原始碼版本:2.6.14,為了方便理解,程式碼都最簡化。
Vue 是如何實現的資料回應式
當你把一個普通的JavaScript 物件傳入Vue 實例作為data 選項,Vue 將會遍歷此對象所有的property,並使用Object.defineProperty 全部將這些property 變成getter/setter,然後圍繞getter/setter來運作。
一句話概括Vue 的響應式系統是: 觀察者模式Object.defineProperty 攔截getter/setter
#什麼是Object.defineProperty ?
Object.defineProperty() 方法會直接在物件上定義一個新屬性,或修改一個物件的現有屬性,並傳回此物件。
obj.xxx 時會觸發get,執行
obj.xxx = xxx會觸發set,這便是響應式的關鍵。
Object.defineProperty 是 ES5 中一個無法 shim(無法透過polyfill實作) 的特性,也就是 Vue 不支援 IE8 以及更低版本瀏覽器的原因。
響應式系統基礎實作
#現在,我們來基於Object.defineProperty實作一個簡易的響應式更新系統作為「開胃菜」
let data = {}; // 使用一个中间变量保存 value let value = "hello"; // 用一个集合保存数据的响应更新函数 let fnSet = new Set(); // 在 data 上定义 text 属性 Object.defineProperty(data, "text", { enumerable: true, configurable: true, set(newValue) { value = newValue; // 数据变化 fnSet.forEach((fn) => fn()); }, get() { fnSet.add(fn); return value; }, }); // 将 data.text 渲染到页面上 function fn() { document.body.innerText = data.text; } // 执行函数,触发读取 get fn(); // 一秒后改变数据,触发 set 更新 setTimeout(() => { data.text = "world"; }, 1000);接下來我們在瀏覽器中運行這段程式碼,會得到期望的效果#透過上面的程式碼,我想你對響應式系統的工作原理已經有了一定的理解。為了讓這個「開胃菜」易於消化,這個簡易的響應式系統還有很多缺點,例如:數據和響應更新函數是透過硬編碼強耦合在一起的、只實現了一對一的情況、不夠模組化等等……所以接下來,我們來一一完善。
設計一個完善的響應式系統
要設計一個完善的響應式系統,我們需要先了解一個前置知識,什麼是觀察者模式?什麼是觀察者模式?
它就是一種行為設計模式, 允許你定義一種訂閱機制, 可在物件事件發生時通知多個 「觀察」 該物件的其他物件。 擁有一些值得關注狀態的對象通常被稱為目標,由於它自身狀態改變時需要通知其他對象,我們也將其成為發布者(publisher) 。所有希望關注發布者狀態變化的其他物件稱為訂閱者(subscribers) 。此外,發布者與所有訂閱者直接僅透過介面交互,都必須具有相同的介面。
抽象觀察者(訂閱者)類別Watcher#
在上面的例子中,数据和响应更新函数是通过硬编码强耦合在一起的。而实际开发过程中,更新函数不一定叫fn
,更有可能是一个匿名函数。所以我们需要抽像一个观察者(订阅者)类Watcher
来保存并执行更新函数,同时向外提供一个update
更新接口。
// Watcher 观察者可能有 n 个,我们为了区分它们,保证唯一性,增加一个 uid let watcherId = 0; // 当前活跃的 Watcher let activeWatcher = null; class Watcher { constructor(cb) { this.uid = watcherId++; // 更新函数 this.cb = cb; // 保存 watcher 订阅的所有数据 this.deps = []; // 初始化时执行更新函数 this.get(); } // 求值函数 get() { // 调用更新函数时,将 activeWatcher 指向当前 watcher activeWatcher = this; this.cb(); // 调用完重置 activeWatcher = null; } // 数据更新时,调用该函数重新求值 update() { this.get(); } }
抽象被观察者(发布者)类Dep
我们再想一想,实际开发过程中,data 中肯定不止一个数据,而且每个数据,都有不同的订阅者,所以说我们还需要抽象一个被观察者(发布者)Dep
类来保存数据对应的观察者(Watcher
),以及数据变化时通知观察者更新。
class Dep { constructor() { // 保存所有该依赖项的订阅者 this.subs = []; } addSubs() { // 将 activeWatcher 作为订阅者,放到 subs 中 // 防止重复订阅 if(this.subs.indexOf(activeWatcher) === -1){ this.subs.push(activeWatcher); } } notify() { // 先保存旧的依赖,便于下面遍历通知更新 const deps = this.subs.slice() // 每次更新前,清除上一次收集的依赖,下次执行时,重新收集 this.subs.length = 0; deps.forEach((watcher) => { watcher.update(); }); } }
抽象 Observer
现在,Watcher
和Dep
只是两个独立的模块,我们怎么把它们关联起来呢?
答案就是Object.defineProperty
,在数据被读取,触发get
方法,Dep 将当前触发 get 的 Watcher 当做订阅者放到 subs中,Watcher
就与 Dep
建立关系;在数据被修改,触发set
方法,Dep
就遍历 subs 中的订阅者,通知Watcher
更新。
下面我们就来完善将数据转换为getter/setter的处理。
上面基础的响应式系统实现中,我们只定义了一个响应式数据,当 data 中有其他property时我们就处理不了了。所以,我们需要抽象一个 Observer
类来完成对 data数据的遍历,并调用defineReactive
转换为 getter/setter,最终完成响应式绑定。
为了简化,我们只处理data中单层数据。
class Observer { constructor(value) { this.value = value; this.walk(value); } // 遍历 keys,转换为 getter/setter walk(obj) { const keys = Object.keys(obj); for (let i = 0; i <p>这里我们通过参数 value 的闭包,来保存最新的数据,避免新增其他变量</p><pre class="brush:php;toolbar:false">function defineReactive(target, key, value) { // 每一个数据都是一个被观察者 const dep = new Dep(); Object.defineProperty(target, key, { enumerable: true, configurable: true, // 执行 data.xxx 时 get 触发,进行依赖收集,watcher 订阅 dep get() { if (activeWatcher) { // 订阅 dep.addSubs(activeWatcher); } return value; }, // 执行 data.xxx = xxx 时 set 触发,遍历订阅了该 dep 的 watchers, // 调用 watcher.updata 更新 set(newValue) { // 如果前后值相等,没必要跟新 if (value === newVal) { return; } value = newValue; // 派发更新 dep.notify(); }, }); }
至此,响应式系统就大功告成了!!
测试
我们通过下面代码测试一下:
let data = { name: "张三", age: 18, address: "成都", }; // 模拟 render const render1 = () => { console.warn("-------------watcher1--------------"); console.log("The name value is", data.name); console.log("The age value is", data.age); console.log("The address value is", data.address); }; const render2 = () => { console.warn("-------------watcher2--------------"); console.log("The name value is", data.name); console.log("The age value is", data.age); }; // 先将 data 转换成响应式 new Observer(data); // 实例观察者 new Watcher(render1); new Watcher(render2);
在浏览器中运行这段代码,和我们期望的一样,两个render
都执行了,并且在控制台上打印了结果。
我们尝试修改 data.name = '李四 23333333'
,测试两个 render
都会重新执行:
我们只修改 data.address = '北京'
,测试一下是否只有render 1
回调都会重新执行:
都完美通过测试!!?
总结
Vue
响应式原理的核心就是Observer
、Dep
、Watcher
,三者共同构成 MVVM 中的 VM
Observer
中进行数据响应式处理以及最终的Watcher
和Dep
关系绑定,在数据被读的时候,触发get
方法,将 Watcher
收集到 Dep
中作为依赖;在数据被修改的时候,触发set
方法,Dep
就遍历 subs 中的订阅者,通知Watcher
更新。
本篇文章属于入门篇,并非源码实现,在源码的基础上简化了很多内容,能够便于理解Observer
、Dep
、Watcher
三者的作用和关系。
本文的源码,以及作者学习 Vue 源码完整的逐行注释源码地址:github.com/yue1123/vue…
以上是一文聊聊Vue響應式實作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Vue.js通過多種功能提升用戶體驗:1.響應式系統實現數據即時反饋;2.組件化開發提高代碼復用性;3.VueRouter提供平滑導航;4.動態數據綁定和過渡動畫增強交互效果;5.錯誤處理機制確保用戶反饋;6.性能優化和最佳實踐提升應用性能。

Vue.js在Web開發中的角色是作為一個漸進式JavaScript框架,簡化開發過程並提高效率。 1)它通過響應式數據綁定和組件化開發,使開發者能專注於業務邏輯。 2)Vue.js的工作原理依賴於響應式系統和虛擬DOM,優化性能。 3)實際項目中,使用Vuex管理全局狀態和優化數據響應性是常見實踐。

Vue.js是由尤雨溪在2014年發布的漸進式JavaScript框架,用於構建用戶界面。它的核心優勢包括:1.響應式數據綁定,數據變化自動更新視圖;2.組件化開發,UI可拆分為獨立、可複用的組件。

Netflix使用React作為其前端框架。 1)React的組件化開發模式和強大生態系統是Netflix選擇它的主要原因。 2)通過組件化,Netflix將復雜界面拆分成可管理的小塊,如視頻播放器、推薦列表和用戶評論。 3)React的虛擬DOM和組件生命週期優化了渲染效率和用戶交互管理。

Netflix在前端技術上的選擇主要集中在性能優化、可擴展性和用戶體驗三個方面。 1.性能優化:Netflix選擇React作為主要框架,並開發了SpeedCurve和Boomerang等工具來監控和優化用戶體驗。 2.可擴展性:他們採用微前端架構,將應用拆分為獨立模塊,提高開發效率和系統擴展性。 3.用戶體驗:Netflix使用Material-UI組件庫,通過A/B測試和用戶反饋不斷優化界面,確保一致性和美觀性。

NetflixusesAcustomFrameworkcalled“ Gibbon” BuiltonReact,notReactorVuedIrectly.1)TeamSperience:selectBasedonFamiliarity.2)ProjectComplexity:vueforsimplerprojects:reactforforforproproject,reactforforforcompleplexones.3)cocatizationneedneeds:reactoffipicatizationneedneedneedneedneedneeds:reactoffersizationneedneedneedneedneeds:reactoffersizatization needefersmoreflexibleise.4)

Netflix在框架選擇上主要考慮性能、可擴展性、開發效率、生態系統、技術債務和維護成本。 1.性能與可擴展性:選擇Java和SpringBoot以高效處理海量數據和高並發請求。 2.開發效率與生態系統:使用React提升前端開發效率,利用其豐富的生態系統。 3.技術債務與維護成本:選擇Node.js構建微服務,降低維護成本和技術債務。

Netflix主要使用React作為前端框架,輔以Vue用於特定功能。 1)React的組件化和虛擬DOM提升了Netflix應用的性能和開發效率。 2)Vue在Netflix的內部工具和小型項目中應用,其靈活性和易用性是關鍵。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3漢化版
中文版,非常好用

Dreamweaver Mac版
視覺化網頁開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器