国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解VueJS 數(shù)據(jù)驅(qū)動(dòng)和依賴追蹤分析

2019-11-19 15:58:46
字體:
供稿:網(wǎng)友

之前關(guān)于 Vue 數(shù)據(jù)綁定原理的一點(diǎn)分析,最近需要回顧,就順便發(fā)到隨筆上了

在之前實(shí)現(xiàn)一個(gè)自己的Mvvm中,用 setter 來觀測(cè)model,將界面上所有的 viewModel 綁定到 model 上。 當(dāng)model改變,更新所有的viewModel,將新值渲染到界面上 。同時(shí)監(jiān)聽界面上通過v-model 綁定的所有 input,并通過 addEventListener事件將新值更新到 model 上,以此來完成雙向綁定 。

但是那段程序除了用來理解 defineProperty,其它一文不值。

  1. 沒有編譯節(jié)點(diǎn) 。
  2. 沒有處理表達(dá)式依賴 。

這里我將解決表達(dá)式依賴這個(gè)問題,vue 模板的編譯我會(huì)在下一節(jié)介紹 。

為數(shù)據(jù)定義 getter & setter

class Observer {  constructor(data) {    this._data = data;    this.walk(this._data);  }  walk(data) {    Object.keys(data).forEach((key) => { this.defineRective(data, key, data[key]) })  };  defineRective(vm, key, value) {    var self = this;    if (value && typeof value === "object") {      this.walk(value);    }    Object.defineProperty(vm, key, {      get: function() {        return value;      },      set: function(newVal) {        if (value != newVal) {          if (newVal && typeof newVal === "object") {            self.walk(newVal);          }          value = newVal;        }      }    })  }}module.exports = Observer;

這樣,就為每個(gè)屬性添加了 getter 和 setter ,當(dāng)屬性是一個(gè)對(duì)象,那么就遞歸添加。

一旦獲取屬性值或者為屬性賦值就會(huì)觸發(fā) get 或 set ,當(dāng)觸發(fā)了 set,即model變化,就可以發(fā)布一個(gè)消息,通知所有viewModel 更新。

defineRective(vm, key, value) {  // 將這個(gè)屬性的依賴表達(dá)式存儲(chǔ)在閉包中。  var dep = new Dep();  var self = this;  if (value && typeof value === "object") {    this.walk(value);  }  Object.defineProperty(vm, key, {    get: function() {      return value;    },    set: function(newVal) {      if (value != newVal) {        if (newVal && typeof newVal === "object") {          self.walk(newVal);        }        value = newVal;        // 通知所有的 viewModel 更新        dep.notify();      }    }  })}

那么怎么定義 Dep 呢??

class Dep {  constructor() {    // 依賴列表    this.dependences = [];  }  // 添加依賴  addDep(watcher) {    if (watcher) {      this.dependences.push(watcher);    }  }  // 通知所有依賴更新  notify() {    this.dependences.forEach((watcher) => {      watcher.update();    })  }}module.exports = Dep;

這里的每個(gè)依賴就是一個(gè)Watcher 。

看看如何定義 Watcher

這里每一個(gè) Watcher 都會(huì)有一個(gè)唯一的id號(hào),它擁有一個(gè)表達(dá)式和一個(gè)回調(diào)函數(shù) 。

比如 表達(dá)式 a +b ; 會(huì)在get 計(jì)算時(shí) 訪問 a 與 b , 由于 JavaScript是單線程,任一時(shí)刻只有一處JavaScript代碼在執(zhí)行, 用Dep.target 作為一個(gè)全局變量來表示當(dāng)前 Watcher 的表達(dá)式,然后通過 compute 訪問 a ,b ,觸發(fā) a 與b 的getter,在 getter 里面將 Dep.target 添加為依賴 。

一旦 a 與 b 的set 觸發(fā),調(diào)用 update 函數(shù),更新依賴的值 。

var uid = 0;class Watcher {  constructor(viewModel, exp, callback) {    this.viewModel = viewModel;    this.id = uid++;    this.exp = exp;    this.callback = callback;    this.oldValue = "";    this.update();  }  get() {    Dep.target = this;    var res = this.compute(this.viewModel, this.exp);    Dep.target = null;    return res;  }  update() {    var newValue = this.get();    if (this.oldValue === newValue) {      return;    }    // callback 里進(jìn)行Dom 的更新操作    this.callback(newValue, this.oldValue);    this.oldValue = newValue;  }  compute(viewModel, exp) {    var res = replaceWith(viewModel, exp);    return res;  }}module.exports = Watcher;

由于當(dāng)前表達(dá)式需要在 當(dāng)前的model下面執(zhí)行,所以 采用replaceWith 函數(shù)來代替 with ,具體可以查看另一篇隨筆javascript 中 with 的替代語法

通過get 添加依賴

Object.defineProperty(vm, key, {  get: function() {    var watcher = Dep.target;    if (watcher && !dep.dependences[watcher.id]) {      dep.addDep(watcher);    }    return value;  },  set: function(newVal) {    if (value != newVal) {      if (newVal && typeof newVal === "object") {        self.walk(newVal);      }      value = newVal;      dep.notify();    }  }})

這種添加依賴的方式實(shí)在太巧妙了 。

 這里我畫了一個(gè)圖來描述

最后通過一段代碼簡(jiǎn)單測(cè)試一下

const Observer = require('./Observer.js');const Watcher = require('./watcher.js');var data = {  a: 10,  b: {    c: 5,    d: {      e: 20,    }  }}var observe = new Observer(data);var watcher = new Watcher(data, "a+b.c", function(newValue, oldValue) {  console.log("new value is " + newValue);  console.log("oldValue is " + oldValue);});console.log("/r/n");console.log("a has changed to 50,then the expr should has value 55");data.a = 50;console.log("/r/n");console.log("b.c has changed to 50,then the expr should has value 122");data.b.c = 72;;console.log("/r/n");console.log("b.c has reseted an object,then the expr should has value 80");data.b = { c: 30 }

OK 大功告成

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 汉中市| 阳春市| 葫芦岛市| 景德镇市| 介休市| 三原县| 虞城县| 龙井市| 万盛区| 安岳县| 延安市| 洪湖市| 林州市| 彭州市| 平顶山市| 佛山市| 沅江市| 博白县| 鱼台县| 都匀市| 重庆市| 额尔古纳市| 会昌县| 昌宁县| 隆尧县| 青川县| 青铜峡市| 许昌市| 阿坝县| 宿州市| 隆安县| 白玉县| 辽阳市| 高淳县| 定结县| 惠东县| 阳西县| 馆陶县| 万载县| 太和县| 太和县|