vue響應式原理

1.數據雙向綁定及響應式

(1).Object.defineProperty
該方法是Object對象定義屬性的方法,能夠修改屬性默認的特性javascript

/*參數解釋:
obj表示目標對象,即屬性所在的對像;
key表示屬性名;
最後一個參數表示屬性的描述符對象
*/
Object.defineProperty(obj, key, {
  configurable: false, //屬性是否能夠被刪除或修改,默認爲false
  enumerable: false, //屬性是否可枚舉,默認爲false
  writeable: true, //屬性的值可否修改,默認爲true
  value: 'yayaya', //包含該屬性的數據值
  get: function() {}, //獲取屬性的方法
  set: function() {} //設置屬性的方法
})

(2).觀察者模式(發佈-訂閱)
(3).舉例解析雙向綁定
<1>.響應式基本原理
例1:
基本思路:渲染視圖 <=監聽每一個data的數據變化 <=遍歷data中的每一項 <=獲取數據html

html中代碼:
    <!DOCTYPE html>
    <html>
      <head>
        <title>demo</title>
      </head>
      <body>
        <div id="demo">
      </body>
    </html>
    <script type="text/javascript" src="vue2.js"></script>
    <script>
    //new 一個 Vue 對象,就會將 data 中的數據進行「 響應式」化
    var v = new Vue({
      el: '#demo',
      data: {
        name: '123'
      },
    })
    v._data.name = '456';
    </script>

vue2.js中代碼:vue

function Vue(options) { //獲取數據
  this._data = options.data;
  observe(this._data);
}

function observe(value) { //遍歷data中的每一項
  //先不考慮value爲數組的複雜狀況
  if (!value || typeof value !== 'object') return;
  Object.keys(value).forEach(function(propertyName) {
    defineReactive(value, propertyName, value[propertyName])
  })
}

function defineReactive(obj, key, val) { //監聽每一個data的數據變化
  console.log('defineReactive==parameter', obj, key, val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    set: function(newVal) {
      console.log('newVal', newVal);
      if (val == newVal) return;
      update(); //若是新修改的值和原來的值不一樣就從新渲染頁面
    },
    get: function() {
      return val;
    }
  })
}

function update() {
  console.log("update view===")
}

輸出結果:
clipboard.pngjava

小結:從data到視圖view的響應式原理主要用到的是Object.definePorperty對數據進行劫持,再更新視圖數組

<2>.依賴收集
1>爲何要有依賴收集this

例1:
var globalData = {
  name: 'yayaya',
  age: '26'
}
var vue1 = new Vue({
  data: {
    name: globalData.name
  }
})
var vue2 = new Vue({
  data: {
    name: globalData.name
  }
})

clipboard.png

假如咱們修改了globalData中name的值,那麼vue1和vue2實例中用到全局name的地方都須要更新,這個時候就須要依賴收集,告訴觀察者哪些實例下的數據須要更新.spa

2>.依賴收集的實現過程雙向綁定

1.訂閱者Dep:Dep的主要做用是存放Watcher觀察者對象 
function Dep() {
 //添加觀察者watcher
  this.subs = [];
  this.addSub = function(sub) {
    this.subs.push(sub);
    console.log("sub==", sub);
  }
  //通知Watcher更新視圖
  this.notify = function() {
    this.subs.forEach(function(s) {
      s.update()
    });
  }
}
Dep.target = null;

2.觀察者Watcher:主要做用是監聽數據變化更新視圖
function Watcher() {
  Dep.target = this; //在new 一個Watcher 對象時將該對象賦值給Dep.target,在get 中會用到
  this.update = function() {
    console.log('updata view===')
  }
}

3.在defineReactivez和Vue中實現依賴收集
 function defineReactive(obj, key, val) { //監聽data中的數據變化
  var dep = new Dep();//實例化一個訂閱者
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    set: function(newVal) {
      //在set時發送通知給Watcher,從新渲染視圖
      console.log("set start===");
      if (val == newVal) return;
      dep.notify(); //若是新修改的值和原來的值不一樣就從新渲染頁面
    },
    get: function() {
      //get時添加依賴
      console.log("get start===");
      dep.addSub(Dep.target); //把watcher實例添加到依賴中
      return val;
    }
  })
}

function Vue(options) { //獲取數據
  this._data = options.data;
  observe(this._data);
  new Watcher();
  console.log("this._data.name", this._data.name)
}

function observe(value) { //遍歷data中的每一項
  //先不考慮value爲數組的複雜狀況
  if (!value || typeof value !== 'object') return;
    Object.keys(value).forEach(function(propertyName) {
    defineReactive(value, propertyName, value[propertyName])
  })
}

4.組件中的代碼
var v = new Vue({
  el: '#demo',
  data: {
    name: '123'
  },
})
console.log("name is changing")
v._data.name = '456';

輸出結果:
clipboard.pngcode

小結:
首先vue在observe中註冊並調用get,在get中,將Watcher實例經過addSub方法添加到dep的subs數組中,這完成了依賴添加的過程; 當數據變化時,調用Object.defineProperty中的set方法,判斷數據是否發生改變,若是改變了,就調用dep中的notify方法通知Watcher,Watcher知道數據變化更新數據.
clipboard.pnghtm

相關文章
相關標籤/搜索