目前前端社區比較推崇的框架有Vue 和 React,公司內部許多端都自發的將原有的老技術方案(widget + jQuery)遷移到 Vue / React上了。
我以爲Vue / React 有如下幾點優點javascript
首先它們都有完整的組件化方案前端
virtual Dom (前端性能提高利器)vue
成熟的社區生態java
上面的例子咱們初始化了一個vue組件,當咱們改變這個組件的狀態時,頁面的內容也會隨之改變,這中間並不須要咱們手動的去操做頁面上的dom元素。
編程
同時咱們注意到Vue提供了一個語法糖 ——watch,這個就是咱們今天要講的 Vue響應式數據流
的主角!代碼很簡單,就是組件的狀態 name 改變的時候咱們輸出一句話 「name change」。
下面咱們會向你們解釋清楚爲何這個 watch 這麼重要,以及它和 Vue的響應式數據流有什麼關係。設計模式
Vue 和 React 都是前端的組件化框架,功能上大同小異,本質上就是藉助virtual Dom幫助開發者管理混亂的Dom,並提供給開發者像操做狀態機同樣操做頁面的能力。框架
可是Vue的virtual Dom 不是普通的 virtual Domdom
Vue 2.0 的實現有不同凡響的地方。和 Vue 的響應式系統結合在一塊兒以後,它可讓你沒必要作任何事就得到徹底優化的重渲染。因爲每一個組件都會在渲染時追蹤其響應依賴,因此係統精確地知道應該什麼時候重渲染、應該重渲染哪些組件。不須要
shouldComponentUpdate
,也不須要 immutable 數據 - it just works . —— 尤雨溪前端性能
咱們看一下第三方的性能分析:
函數
除了性能,最大的優點是減輕了開發者的負擔,開發者大多數狀況下不須要依賴 shouldComponentUpdate
,也不須要依賴 immutable 數據去判斷組件是否須要從新渲染,Vue會幫你作好這件事。
舉個例子來講明這兩個的virtual dom的不一樣之處:
開發者就像一個老師,Vue和React這兩個學生要作的事就是根據老師給出的長寬畫出對應的長方形。每當老師改變給出的長和寬時,Vue可以本身發現長和寬變沒變,需不須要從新畫;React則須要老師告訴它長和寬變了,須要從新畫了。
JavaScript 提供一個很是強大的方法 Object.defineProperty,它能夠定義當某一個值訪問和賦值時會先執行自定義的鉤子方法。
var obj = {}; var initValue = 'hello'; Object.defineProperty(obj,"newKey",{ get:function (){ //當獲取值的時候觸發的函數 return initValue; }, set:function (value){ //當設置值的時候觸發的函數,設置的新值經過參數value拿到 console.log(value) initValue = value; } }); //獲取值 console.log( obj.newKey ); //hello //設置值 obj.newKey = 'change value'; //change value
這個方法給予JavaScript開發一種面向切面編程的能力,使用該方法咱們可以隱式、天然的控制屬性的訪問和賦值。
訂閱發佈是一個很是常見的設計模式,原理也很是簡單就是訂閱者訂閱信息,而後發佈者發佈信息通知訂閱者更新。
前面鋪墊這麼多就是但願你們能理解接下來要講的響應式數據流。
如上圖,Vue的初始化會執行一系列的方法,這裏咱們主要介紹Vue的initState 方法。
prop和data都是組件的屬性,prop一般上是父組件傳遞下來的,data是組件自身定義的,Vue不推薦你去改組件傳遞下來的prop,由於那樣會帶來沒必要要的複雜度。
Prop和data 的最終歸宿都是遞歸執行 defineReactive方法。
那defineReactive方法作了什麼呢?
defineReactive會用 Object.defineProperty將組件的每一個屬性都包裝一下,這樣誰訪問了這些屬性,誰從新賦值了這些屬性咱們都能追蹤到了。
Vue裏面有一個 Observe類,全部的prop子屬性和data自己都會帶有一個Observer對象,Observer的構造函數
在控制檯咱們能夠看到每一個屬性下都有__ob__,這說明這個屬性已經被包裝成 Observer對象了,因此的訪問和賦值都能給追蹤到,這裏面也保存着全部訂閱該Observer的訂閱者Watcher。
咱們看一下Watcher的構造函數
Watcher支持 watch 一個表達式或者是一個方法。Watcher在構造的時候會先獲取一次expOrFn的值,下面咱們把expOrFn稱爲Watcher的Getter。
還有一個關鍵的類是Dep,這個類會幫助咱們的屬性記錄下全部的Watcher,每一個屬性都有本身的Dep實例,同時Vue的Watcher訪問的屬性的時候 Dep會做爲一個全局變量將自身的target屬性指向訪問的Wathcer。會執行下面的方法
同時咱們再回來看 defineReactive這個重要的方法
當Watcher訪問組件的屬性時,經過Dep.target,Vue能夠知道是Watcher訪問的, 這樣當Vue本身的Watcher訪問屬性的時候會被記錄成訂閱者,而咱們訪問的時候Vue不會執行多餘的代碼。這是一個很精妙的設計,將Object.defineProperty 與 訂閱發佈設計模式結合起來了。
看一下整個的流程圖
理解了以上Vue是如何將Object.defineProperty 與 訂閱發佈設計模式結合起來的,而後咱們再觸類旁通:Vue的render函數若是就是 Watcher 的 expOrFn會怎麼樣?
回到Vue的源碼裏:
這裏的 vm._render就是 render函數的一個封裝,咱們能夠看到本質上:Vue的render函數就是 Watcher 的 expOrFn。那初始化的時候咱們會先執行一邊 render函數,在執行render函數的過程當中訪問了哪些 組件的屬性,Vue都會用上面的提到的方法幫咱們把依賴記錄下來。因此當這個屬性變化的時候,天然而然,就像文章開頭的watch同樣,咱們會從新render一次,(開頭的例子是輸出「name change」)。
講到這裏你們應該都可以明白Vue的響應式數據流是如何實現的。同時咱們可以發現Vue提供給咱們的許多語法糖都是一樣的道理,好比Vue的computer就是將computer函數做爲Watcher的expOrFn。但願你們在理解Vue響應式數據流的基礎上可以更加自信、靈活和穩健的使用這個優秀的框架。