淺談Vue響應式原理

Vue.js 的核心包括一套「響應式系統」。數組

「響應式」,是指當數據改變後,Vue 會通知到使用該數據的代碼。例如,視圖渲染中使用了數據,數據改變後,視圖也會自動更新。app

對於官網上關於響應式數據的描述,並不能讓人短期內明白其原理。下面我將按照個人理解分析一下Vue2.0響應式核心代碼實現。ide

Vue中響應式數據分爲:對象類型{}和數組類型[]函數

對象類型 {}

咱們若想要實現響應式,須要如下類和方法:性能

  • 數據data
  • 數據監聽器defineReactive
  • 訂閱者(更新視圖)watcher
  • 維護訂閱者dep
  • 狀態active

實現原理: 對象內部經過defineReactive方法,使用Object.defineProperty將屬性進行劫持(只會劫持已經存在的屬性)。this

假設頁面上有容器app,data存放響應式變量,當data中的值改變時,容器內的數值也會發生變化。prototype

    <div id="app"></div>    <script>let data = {count:0}
        app.innerHTML = data.count</script>複製代碼

1.添加視圖更新watcher

    <div id="app"></div><script>let data = {count:0}// 定義watcher函數,傳入參數爲函數,且當即執行let watcher = (fn)=>{
            fn();
        }

        watcher(()=>{// 更換app內容app.innerHTML = data.count;
        })</script>複製代碼

watcher所執行的操做 將頁面上的內容更新=>視圖更新對象

2.實現數據監聽器

將data中的屬性依次增長get()和set()方法,這樣當用戶取值的時候,看成模版收集起來。待數據變化通知模版數據更新。blog

    <script>let data = {count:0}// 數據監聽器function defineReactive(obj) {//每個屬性都從新定義get、setfor(let key in obj){            	let value = obj[key]Object.defineProperty(obj,key,{// 當data中的值「出現」的時候,執行get()get(){                    	//將獲取的原始值返回return value;
                    },                     // 當data中的值「改變」的時候,執行get()set(newValue){
                        value = newValue
                    }
                })
            }
        }//劫持data中的數據defineReactive(data)//此時的a沒有被數據監聽器監測到,屬於「後來者」不受劫持data.a = 10;// 定義watcher函數,傳入參數爲函數,且當即執行let watcher = (fn)=>{
            fn();
        }

        watcher(()=>{// 取值app.innerHTML = data.count;
        })
    </script>複製代碼

3. 當數據改變時更新視圖

   <div id="app"></div>   <script>   let data = {           count:0   }       //須要執行的視圖內容   let active;       // 數據監聽器   function defineReactive(obj) {           for(let key in obj){               let value = obj[key];               //存放當前屬性相關的全部方法   let dep = [];               Object.defineProperty(obj,key,{                   // 當data中的值「出現」的時候,執行get()   get(){                       //(3)   if(active){
                           dep.push(active)
                       }                       return value
                   },// 當data中的值「改變」的時候,執行get()   set(newValue){
                       value = newValue
                       dep.forEach(active=>active())
                   }
               })
           }
       }       //劫持data中的數據   defineReactive(data)       // 定義watcher函數,傳入參數爲函數,且當即執行   let watcher = (fn)=>{
           active = fn;           //(1)   fn();           //(4)   active = null;
       }

       watcher(()=>{           // 更換app內容   //(2)   app.innerHTML = data.count;
       })   </script>複製代碼

當定義watcher時,會依次執行(1)=>(2)=>(3)=>(4)。 每一個屬性都擁有本身的dep屬性,存放它所存放的watcher,當屬性變化後會同志本身對應的watcher去更新。遞歸

Vue2.0響應式用的是Object.definePropertyVue3.0響應式用的是proxy

當data中的數據存在多層嵌套的時候,若是用Object.defineProperty,內部會進行遞歸,影響性能。proxy提高性能,可是不兼容ie11。

數組類型 []

數組考慮性能緣由沒有用defineProperty對數組的每一項進行攔截,而是選擇對數組原型上的方法進行重寫(push,pop,shift,unshift,splice,sort,reverse)只有這7種方法會重寫數組

    <div id="app"></div><script>let data = [1,2,3];// 獲取數組全部方法-原型鏈let originArray = Array.prototype;// 淺拷貝let arrayMethods = Object.create(originArray);// 數據監聽器function defineReactive(obj) {// 函數劫持,重寫方法。能夠添加本身想要執行的內容arrayMethods.push = function (...args) {// 更改this指向originArray.push.call(this,...args);
                render();
            }// 掛載到原型上obj.__proto__ = arrayMethods
        }
        defineReactive(data)// 視圖渲染function render() {
            app.innerHTML = data;
        }
        render();       </script>複製代碼

在Vue中修改數組的索引和長度是沒法監控到的。須要經過以上7種變異方法修改數組纔會觸發數組對應的watcher進行更新。數組中若是是對象類型也會進行遞歸劫持。

若是想要更改索引,能夠經過Vue.$set來進行處理,內部核心代碼是splice方法

相關文章
相關標籤/搜索