Vue.js 的核心包括一套「響應式系統」。數組
「響應式」,是指當數據改變後,Vue 會通知到使用該數據的代碼。例如,視圖渲染中使用了數據,數據改變後,視圖也會自動更新。app
對於官網上關於響應式數據的描述,並不能讓人短期內明白其原理。下面我將按照個人理解分析一下Vue2.0響應式核心代碼實現。ide
Vue中響應式數據分爲:對象類型{}和數組類型[]函數
咱們若想要實現響應式,須要如下類和方法:性能
實現原理: 對象內部經過defineReactive方法,使用Object.defineProperty將屬性進行劫持(只會劫持已經存在的屬性)。this
假設頁面上有容器app,data存放響應式變量,當data中的值改變時,容器內的數值也會發生變化。prototype
<div id="app"></div> <script>let data = {count:0} app.innerHTML = data.count</script>複製代碼
<div id="app"></div><script>let data = {count:0}// 定義watcher函數,傳入參數爲函數,且當即執行let watcher = (fn)=>{ fn(); } watcher(()=>{// 更換app內容app.innerHTML = data.count; })</script>複製代碼
watcher所執行的操做 將頁面上的內容更新=>視圖更新對象
將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>複製代碼
<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方法