亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Vue監聽數據對象變化源碼

2019-11-19 17:13:30
字體:
來源:轉載
供稿:網友

監聽數據對象變化,最容易想到的是建立一個需要監視對象的表,定時掃描其值,有變化,則執行相應操作,不過這種實現方式,性能是個問題,如果需要監視的數據量大的話,每掃描一次全部的對象,需要的時間很長。當然,有些框架是采用的這種方式,不過他們用非常巧妙的算法提升性能,這不在我們的討論范圍之類。

Vue 中數據對象的監視,是通過設置 ES5 的新特性(ES7 都快出來了,ES5 的東西倒也真稱不得新)Object.defineProperty() 中的 set、get 來實現的。

目標

與官方文檔第一個例子相似,不過也有簡化,因為這篇只是介紹下數據對象的監聽,不涉及文本解析,所以文本解析相關的直接舍棄了:

<div id="app"></div>var app = new Vue({ el: 'app', data: { message: 'Hello Vue!' }});

瀏覽器顯示:

Hello Vue!

在控制臺輸入諸如:

app.message = 'Changed!'

之類的命令,瀏覽器顯示內容會跟著修改。

Object.defineProperty

引用 MDN 上的定義:

Object.defineProperty()方法會直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性, 并返回這個對象。
與此相生相伴的還有一個 Object.getOwnPropertyDescriptor():

Object.getOwnPropertyDescriptor() 返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)

下面的例子用一種比較簡單、直觀的方式來設置 setter、getter:

var dep = [];function defineReactive(obj, key, val) { // 有自定義的 property,則用自定義的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() {  var value = getter ? getter.call(obj) : val;  dep.push(value);  return value; }, set: function(newVal) {  var value = getter ? getter.call(obj) : val;  // set 值與原值相同,則不更新  if(newVal === value) {  return;  }  if(setter) {  setter.call(obj, newVal);  } else {  val = newVal;  }  console.log(dep); } });}
var a = {};defineReactive(a, 'a', 12);// 調用 getter,12 被壓入 dep,此時 dep 值為 [12]a.a;// 調用 setter,輸出 dep ([12])a.a = 24;// 調用 getter,24 被壓入 dep,此時 dep 值為 [12, 24]a.a;

Observer

簡單說過 Object.defineProperty 之后,就要開始扯 Observer 了。observer,中文解釋為“觀察者”,觀察什么東西呢?觀察對象屬性值的變化。故此,所謂 observer,就是給對象的所有屬性加上 getter、setter,如果對象的屬性還有屬性,比如說 {a: {a: {a: 'a'}}},則通過遞歸給其屬性的屬性也加上 getter、setter:

function Observer(value) { this.value = value; this.walk(value);}Observer.prototype.walk = function(obj) { var keys = Object.keys(obj); for(var i = 0; i < keys.length; i++) { // 給所有屬性添加 getter、setter defineReactive(obj, keys[i], obj[keys[i]]); }};var dep = [];function defineReactive(obj, key, val) { // 有自定義的 property,則用自定義的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; // 遞歸的方式實現給屬性的屬性添加 getter、setter var childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() {  var value = getter ? getter.call(obj) : val;  dep.push(value);  return value; }, set: function(newVal) {  var value = getter ? getter.call(obj) : val;  // set 值與原值相同,則不更新  if(newVal === value) {  return;  }  if(setter) {  setter.call(obj, newVal);  } else {  val = newVal;  }  // 給新賦值的屬性值的屬性添加 getter、setter  childOb = observe(newVal);  console.log(dep); } });}function observe(value) { if(!value || typeof value !== 'object') { return; } return new Observer(value);}

Watcher

Observer 通過設置數據對象的 getter、setter 來達到監聽數據變化的目的。數據被獲取,被設置、被修改,都能監聽到,且能做出相應的動作。

現在還有一個問題就是,誰讓你監聽的?

這個發出指令的就是 Watcher,只有 Watcher 獲取數據才觸發相應的操作;同樣,修改數據時,也只執行 Watcher 相關操作。

那如何講 Observer、Watcher 兩者關聯起來呢?全局變量!這個全局變量,只有 Watcher 才做修改,Observer 只是讀取判斷,根據這個全局變量的值不同而判斷是否 Watcher 對數據進行讀取,這個全局變量可以附加在 dep 上:

dep.target = null;

根據以上所述,簡單整理下,代碼如下:

function Watcher(data, exp, cb) { this.data = data; this.exp = exp; this.cb = cb; this.value = this.get();}Watcher.prototype.get = function() { // 給 dep.target 置值,告訴 Observer 這是 Watcher 調用的 getter dep.target = this; // 調用 getter,觸發相應響應 var value = this.data[this.exp]; // dep.target 還原 dep.target = null; return value;};Watcher.prototype.update = function() { this.cb();};function Observer(value) { this.value = value; this.walk(value);}Observer.prototype.walk = function(obj) { var keys = Object.keys(obj); for(var i = 0; i < keys.length; i++) { // 給所有屬性添加 getter、setter defineReactive(obj, keys[i], obj[keys[i]]); }};var dep = [];dep.target = null;function defineReactive(obj, key, val) { // 有自定義的 property,則用自定義的 property var property = Object.getOwnPropertyDescriptor(obj, key); if(property && property.configurable === false) { return; } var getter = property && property.get; var setter = property && property.set; // 遞歸的方式實現給屬性的屬性添加 getter、setter var childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function() {  var value = getter ? getter.call(obj) : val;  // 如果是 Watcher 監聽的,就把 Watcher 對象壓入 dep  if(dep.target) {  dep.push(dep.target);  }  return value; }, set: function(newVal) {  var value = getter ? getter.call(obj) : val;  // set 值與原值相同,則不更新  if(newVal === value) {  return;  }  if(setter) {  setter.call(obj, newVal);  } else {  val = newVal;  }  // 給新賦值的屬性值的屬性添加 getter、setter  childOb = observe(newVal);  // 按序執行 dep 中元素的 update 方法  for(var i = 0; i < dep.length; i++) {  dep[i].update();   } } });}function observe(value) { if(!value || typeof value !== 'object') { return; } return new Observer(value);}
var data = {a: 1};new Observer(data);new Watcher(data, 'a', function(){console.log('it works')});data.a =12;data.a =14;

上面基本實現了數據的監聽,bug 肯定有不少,不過只是一個粗糙的 demo,只是想展示一個大概的流程,沒有扣到非常細致。

Dep

上面幾個例子,dep 是個全局的數組,但凡 new 一個 Watcher,dep 中就要多一個 Watcher 實例,這時候不管哪個 data 更新,所有的 Watcher 實例的 update 都會執行,這是不可接受的。

Dep 抽象出來,單獨搞一個構造函數,不放在全局,就能解決了:

function Dep() { this.subs = [];}Dep.prototype.addSub = function(sub) { this.subs.push(sub);};Dep.prototype.notify = function() { var subs = this.subs.slice(); for(var i = 0; i < subs.length; i++) { subs[i].update(); }}

利用 Dep 將上面的代碼改寫下就好了(當然,此處的 Dep 代碼也不完全,只是一個大概的意思罷了)。

Vue 實例代理 data 對象

官方文檔中有這么一句話:

每個 Vue 實例都會代理其 data 對象里所有的屬性。

var data = { a: 1 };var vm = new Vue({data: data});vm.a === data.a // -> true// 設置屬性也會影響到原始數據vm.a = 2data.a // -> 2// ... 反之亦然data.a = 3vm.a // -> 3

這種代理看起來很麻煩,其實也是可以通過 Object.defineProperty 來實現的:

function Vue(options) { var data = this.data = options.data; var keys = Object.keys(data); var i = keys.length; while(i--) { proxy(this, keys[i]; }}function proxy(vm, key) { Object.defineProperty(vm, key, { configurable: true, enumerable: true, // 直接獲取 vm.data[key] 的值 get: function() {  return vm.data[key]; }, // 設置值的時候直接設置 vm.data[key] 的值 set: function(val) {  vm.data[key] = val; } };}

捏出一個 Vue,實現最初目標

var Vue = (function() { var Watcher = function Watcher(vm, exp, cb) {  this.vm = vm;  this.exp = exp;  this.cb = cb;  this.value = this.get(); }; Watcher.prototype.get = function get() {  Dep.target = this;  var value = this.vm._data[this.exp];  Dep.target = null;  return value; }; Watcher.prototype.addDep = function addDep(dep) {  dep.addSub(this); }; Watcher.prototype.update = function update() {  this.run(); }; Watcher.prototype.run = function run() {  this.cb.call(this.vm); } var Dep = function Dep() {  this.subs = []; }; Dep.prototype.addSub = function addSub(sub) {  this.subs.push(sub); }; Dep.prototype.depend = function depend() {  if(Dep.target) {   Dep.target.addDep(this);  } }; Dep.prototype.notify = function notify() {  var subs = this.subs.slice();  for(var i = 0; i < subs.length; i++) {   subs[i].update();  } }; Dep.target = null; var Observer = function Observer(value) {  this.value = value;  this.dep = new Dep();  this.walk(value); }; Observer.prototype.walk = function walk(obj) {  var keys = Object.keys(obj);  for(var i = 0; i < keys.length; i++) {   defineReactive(obj, keys[i], obj[keys[i]]);  } }; function defineReactive(obj, key, val) {  var dep = new Dep();  var property = Object.getOwnPropertyDescriptor(obj, key);  if(property && property.configurable === false) {   return;  }  var getter = property && property.get;  var setter = property && property.set;  var childOb = observe(val);  Object.defineProperty(obj, key, {   enumerable: true,   configurable: true,   get: function reactiveGetter() {    var value = getter ? getter.call(obj) : val;    if(Dep.target) {     dep.depend();     if(childOb) {      childOb.dep.depend();     }    }    return value;   },   set: function reactiveSetter(newVal) {    var value = getter ? getter.call(obj) : val;    if(newVal === value) {     return;    }    if(setter) {     setter.call(obj, newVal);    } else {     val = newVal;    }    childOb = observe(newVal);    dep.notify();   }  }); } function observe(value) {  if(!value || typeof value !== 'object') {   return;  }  return new Observer(value); } function Vue(options) {  var vm = this;  this._el = options.el;  var data = this._data = options.data;  var keys = Object.keys(data);  var i = keys.length;  while(i--) {   proxy(this, keys[i]);  }  observe(data);  var elem = document.getElementById(this._el);  elem.innerHTML = vm.message;  new Watcher(this, 'message', function() {   elem.innerHTML = vm.message;  }); } function proxy(vm, key) {  Object.defineProperty(vm, key, {   configurable: true,   enumerable: true,   get: function proxyGetter() {    return vm._data[key];   },   set: function proxySetter(val) {    vm._data[key] = val;   }  }); } return Vue;})();
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript" src="vue.js"></script></head><body> <div id="app"></div> <script type="text/javascript">  var app = new Vue({   el: 'app',   data: {    message: 'aaaaaaaaaaaaa'   }  }); </script></body></html>

參考資料:

vue 源碼分析之如何實現 observer 和 watcher
vue早期源碼學習系列之一:如何監聽一個對象的變化

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
热久久免费国产视频| 狠狠色狠色综合曰曰| 欧美国产日本在线| 国产日韩在线看片| 亚洲国内精品在线| 成人a级免费视频| 国产情人节一区| 日本国产精品视频| 俺也去精品视频在线观看| 韩国欧美亚洲国产| 色爱av美腿丝袜综合粉嫩av| 日韩在线不卡视频| 午夜美女久久久久爽久久| 亚洲国产高清自拍| 91久久精品视频| 亚洲女人天堂色在线7777| 日韩av中文字幕在线| 欧美肥臀大乳一区二区免费视频| 亚洲a在线观看| 欧美超级乱淫片喷水| 国产精品天天狠天天看| 久久国产视频网站| 亚洲黄色在线观看| 欧美黄色免费网站| 欧美日韩另类在线| 91av视频在线观看| 69**夜色精品国产69乱| 一区二区三区 在线观看视| 91国在线精品国内播放| 日韩欧美在线看| 日韩电影在线观看永久视频免费网站| 欧美裸体xxxx极品少妇| 欧美激情视频免费观看| 97欧美精品一区二区三区| 日韩精品视频在线免费观看| 国外成人在线视频| 久久中文字幕在线| 在线观看欧美www| 57pao国产成人免费| 日韩精品极品在线观看播放免费视频| 日韩在线观看网址| 亚洲国产精彩中文乱码av| 亚洲精品一区中文| 伊人久久大香线蕉av一区二区| 国产+成+人+亚洲欧洲| 国产精品va在线| 国产亚洲精品激情久久| 精品久久久久久久久久久久| 欧美大人香蕉在线| 在线视频欧美日韩| 欧美在线观看网站| 欧美一级视频免费在线观看| 日本久久久a级免费| 国产999精品| 亚洲第一区中文字幕| 日韩高清免费在线| 久久亚洲精品毛片| 国产伦精品免费视频| 2019日本中文字幕| 成人h片在线播放免费网站| 欧美高清第一页| 日韩av网站电影| 久久久爽爽爽美女图片| 91热福利电影| 色老头一区二区三区在线观看| 蜜臀久久99精品久久久久久宅男| 国内精品久久久久久中文字幕| 亚洲国产精品久久久久久| 欧美日韩在线观看视频小说| 尤物yw午夜国产精品视频| 一个人看的www欧美| 欧美午夜女人视频在线| 全色精品综合影院| 久久精品国产久精国产思思| 欧美视频在线看| 国产+成+人+亚洲欧洲| 国产精品丝袜一区二区三区| 午夜精品蜜臀一区二区三区免费| 欧美日韩视频在线| 自拍偷拍亚洲精品| 久久99青青精品免费观看| 精品久久久久久久中文字幕| 91色琪琪电影亚洲精品久久| 日韩三级影视基地| 在线观看国产精品91| 性欧美xxxx视频在线观看| 久久久久久久网站| 精品久久久久久中文字幕大豆网| 精品福利免费观看| 国产精品视频精品| 亚洲成人激情视频| 亚洲精品久久久久久久久久久久久| 久久影院在线观看| 欧美精品福利在线| 色综合影院在线| 国内免费久久久久久久久久久| 亚洲成人激情视频| 国产欧美精品xxxx另类| 日韩精品在线观| 国产精品久在线观看| 亚洲iv一区二区三区| 在线不卡国产精品| 欧美日韩成人在线播放| 日韩成人av在线播放| 国产精品wwww| 久久精品免费电影| 黑人与娇小精品av专区| 日韩在线免费高清视频| 日韩国产中文字幕| 国产精品欧美日韩| 91国产一区在线| 日韩大片在线观看视频| 一区二区亚洲欧洲国产日韩| 亚洲国产欧美精品| 欧美极品少妇xxxxⅹ免费视频| 日韩网站在线观看| 中文字幕少妇一区二区三区| 日韩精品一区二区三区第95| 91禁外国网站| 伊人久久男人天堂| 欧美日韩aaaa| 欧美巨乳美女视频| 久久精品成人欧美大片古装| 国产精品极品美女粉嫩高清在线| 亚洲欧美三级伦理| 日韩中文字幕视频在线| 欧美一级在线亚洲天堂| 亚洲国产天堂久久综合| 精品在线观看国产| 国产精品高潮视频| 亚洲国产精品久久精品怡红院| 午夜精品一区二区三区视频免费看| 中文字幕久热精品在线视频| 久久久久久久久91| 狠狠做深爱婷婷久久综合一区| 欧美做受高潮电影o| 亚洲欧美成人精品| 精品福利在线看| 亚洲国产美女精品久久久久∴| 国产免费一区二区三区在线观看| 日本久久久久久久久久久| 欧美激情在线狂野欧美精品| 国产www精品| 欧美一级片在线播放| 色婷婷av一区二区三区在线观看| 青青久久av北条麻妃海外网| 一区二区亚洲精品国产| 国产精品一区二区久久国产| 91在线视频成人| 国产亚洲精品久久久久久| 久久综合亚洲社区| 亚洲欧美三级在线| 91精品成人久久| 久久天堂av综合合色| 国产精品白丝jk喷水视频一区| 欧美大片免费观看在线观看网站推荐| 国产亚洲精品久久久久动| 91精品久久久久久久久久久| 92裸体在线视频网站| 国产一区二区香蕉| 亚洲国产免费av| 2019中文字幕在线| 欧美日韩亚洲视频| 国产精品吹潮在线观看|