日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
一文搞定數(shù)據(jù)響應(yīng)式原理

在Vue中,其中最最最核心的一個(gè)知識(shí)點(diǎn)就是數(shù)據(jù)響應(yīng)式原理,數(shù)據(jù)響應(yīng)式原理歸結(jié)起來就包含兩大部分:偵測(cè)數(shù)據(jù)變化、依賴收集,了解這兩個(gè)知識(shí)點(diǎn)就了解到了數(shù)據(jù)響應(yīng)式原理的精華。

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比東明網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式東明網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋東明地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。

一、偵測(cè)數(shù)據(jù)變化

能夠幀聽到數(shù)據(jù)變化是數(shù)據(jù)響應(yīng)式原理的前提,因?yàn)閿?shù)據(jù)響應(yīng)式正是基于監(jiān)聽到數(shù)據(jù)變化后來觸發(fā)一系列的更新操作。本次介紹數(shù)據(jù)響應(yīng)式原理將基于Vue2.x進(jìn)行,其將數(shù)據(jù)變?yōu)榭杀粋蓽y(cè)數(shù)據(jù)時(shí)主要采用了Object.defineProperty()。

1.1 非數(shù)組對(duì)象

下面先舉一個(gè)非數(shù)組對(duì)象的例子

 
 
 
 
  1. const obj = {
  2.     a: {
  3.         m: {
  4.             n: 5
  5.         }
  6.     },
  7.     b: 10
  8. };

觀察上面的對(duì)象,可以發(fā)現(xiàn)其是存在包含關(guān)系的(即一個(gè)對(duì)象中可能包含另一個(gè)對(duì)象),那么自然會(huì)想到通過遞歸的方式實(shí)現(xiàn),在Vue中為了保證代碼較高的可讀性,引入了三個(gè)模塊實(shí)現(xiàn)該邏輯:observe、Observer、defineReactive,其調(diào)用關(guān)系如下所示:

1.1.1 observe

這個(gè)函數(shù)是幀聽數(shù)據(jù)變化的入口文件,通過調(diào)用該函數(shù)一方面觸發(fā)了其幀聽對(duì)象數(shù)據(jù)變化的能力;另一方面定義了何時(shí)遞歸到最內(nèi)層的終止條件。

 
 
 
 
  1. import Observer from './Observer';
  2. export default function (value) {
  3.     // 如果value不是對(duì)象,什么都不做(表示該遞歸到的是基本類型,其變化可被幀聽的)
  4.     if (typeof value !== 'object') {
  5.         return;
  6.     }
  7.     // Observer實(shí)例
  8.     let ob;
  9.     // __ob__是value上的屬性,其值就是對(duì)應(yīng)的Observer實(shí)例(表示其已經(jīng)是可幀聽的狀態(tài))
  10.     if (typeof value.__ob__ !== 'undefined') {
  11.         ob = value.__ob__;
  12.     }
  13.     else {
  14.         // 是對(duì)象且該上屬性還是未能夠幀聽狀態(tài)的
  15.         ob = new Observer(value);
  16.     }
  17.     return ob;
  18. }

1.1.2 Observer

這個(gè)函數(shù)的目的主要有兩個(gè):一個(gè)是將該實(shí)例掛載到該對(duì)象value的__ob__屬性上(observe上用到了該屬性,通過判斷是否有該屬性判斷是否已經(jīng)屬于幀聽狀態(tài));另一個(gè)是遍歷該對(duì)象上的所有屬性,然后將該屬性均變?yōu)榭蓭牭?通過調(diào)用defineReactive實(shí)現(xiàn))。

 
 
 
 
  1. export default class Observer {
  2.     constructor(value) {
  3.         // 給實(shí)例添加__ob__屬性
  4.         def(value, '__ob__', this, false);
  5.         // 檢查是數(shù)組還是對(duì)象
  6.         if (!Array.isArray(value)) {
  7.             // 若為對(duì)象,則進(jìn)行遍歷,將其上的屬性變?yōu)轫憫?yīng)式的
  8.             this.walk(value);
  9.         }
  10.     }
  11.     // 對(duì)于對(duì)象上的屬性進(jìn)行遍歷,將其變?yōu)轫憫?yīng)式的
  12.     walk(value) {
  13.         for (let key in value) {
  14.             defineReactive(value, key);
  15.         }
  16.     }
  17. }

1.1.3 defineReactive

這個(gè)方法主要是將Object.defineProperty封裝到一個(gè)函數(shù)中,做這一步操作的原因是因?yàn)镺bject.defineProperty設(shè)置set屬性時(shí)需要一個(gè)臨時(shí)變量來存儲(chǔ)變化前的值,通過封裝利用閉包的思想引入val,這樣就不需要在函數(shù)外面再設(shè)置臨時(shí)變量了。

 
 
 
 
  1. export default function defineReactive(data, key, val) {
  2.     if (arguments.length === 2) {
  3.         val = data[key];
  4.     }
  5.     // 子元素要進(jìn)行observe,至此形成了遞歸
  6.     let childOb = observe(val);
  7.     Object.defineProperty(data, key, {
  8.         // 可枚舉
  9.         enumerable: true,
  10.         // 可配置
  11.         configurable: true,
  12.         // getter
  13.         get() {
  14.             console.log(`訪問${key}屬性`);
  15.             return val;
  16.         },
  17.         // setter
  18.         set(newValue) {
  19.             console.log(`改變${key}的屬性為${newValue}`);
  20.             if (val === newValue) {
  21.                 return;
  22.             }
  23.             val = newValue;
  24.             // 當(dāng)設(shè)置了新值,這個(gè)新值也要被observe
  25.             childOb = observe(newValue);
  26.         }
  27.     });
  28. }

1.2 數(shù)組

Object.defineProperty不能直接監(jiān)聽數(shù)組內(nèi)部的變化,那么數(shù)組內(nèi)容變化應(yīng)該怎么操作呢?Vue主要采用的是改裝數(shù)組方法的方式(push、pop、shift、unshift、splice、sort、reverse),在保留其原有功能的前提下,將其新添加的項(xiàng)變?yōu)轫憫?yīng)式的。

 
 
 
 
  1. // array.js文件
  2. // 得到Array的原型
  3. const arrayPrototype = Array.prototype;
  4. // 以Array.prototype為原型創(chuàng)建arrayMethods對(duì)象,并暴露
  5. export const arrayMethods = Object.create(arrayPrototype);
  6. // 要被改寫的7個(gè)數(shù)組方法
  7. const methodsNeedChange = [
  8.     'push',
  9.     'pop',
  10.     'shift',
  11.     'unshift',
  12.     'splice',
  13.     'sort',
  14.     'reverse'
  15. ];
  16. methodsNeedChange.forEach(methodName => {
  17.     //備份原來的方法
  18.     const original = arrayMethods[methodName];
  19.     // 定義新的方法
  20.     def(arrayMethods, methodName, function () {
  21.         // 恢復(fù)原來的功能
  22.         const result = original.apply(this, arguments);
  23.         // 將類數(shù)組對(duì)象轉(zhuǎn)換為數(shù)組
  24.         const args = [...arguments];
  25.         // 數(shù)組不會(huì)是最外層,所以其上已經(jīng)添加了Observer實(shí)例
  26.         const ob = this.__ob__;
  27.         // push/unshift/splice會(huì)插入新項(xiàng),需要將插入的新項(xiàng)變成observe的
  28.         let inserted = [];
  29.         switch (methodName) {
  30.             case 'push':
  31.             case 'unshift': {
  32.                 inserted = args;
  33.                 break;
  34.             }
  35.             case 'splice': {
  36.                 inserted = args.slice(2);
  37.                 break;
  38.             }
  39.         }
  40.         // 對(duì)于有插入項(xiàng)的,讓新項(xiàng)變?yōu)轫憫?yīng)的
  41.         if (inserted.length) {
  42.             ob.observeArray(inserted);
  43.         }
  44.         ob.dep.notify();
  45.         return result;
  46.     }, false);
  47. });

除了改裝其原有數(shù)組方法外,Observer函數(shù)中也將增加對(duì)數(shù)組的處理邏輯。

 
 
 
 
  1. export default class Observer {
  2.     constructor(value) {
  3.         // 給實(shí)例添加__ob__屬性
  4.         def(value, '__ob__', this, false);
  5.         // 檢查是數(shù)組還是對(duì)象
  6.         if (Array.isArray(value)) {
  7.             // 改變數(shù)組的原型為新改裝的內(nèi)容
  8.             Object.setPrototypeOf(value, arrayMethods);
  9.             // 讓這個(gè)數(shù)組變?yōu)閛bserve
  10.             this.observeArray(value);
  11.         }
  12.         else {
  13.             // 若為對(duì)象,則進(jìn)行遍歷,將其上的屬性變?yōu)轫憫?yīng)式的
  14.             this.walk(value);
  15.         }
  16.     }
  17.     // 對(duì)于對(duì)象上的屬性進(jìn)行遍歷,將其變?yōu)轫憫?yīng)式的
  18.     walk(value) {
  19.         for (let key in value) {
  20.             defineReactive(value, key);
  21.         }
  22.     }
  23.     // 數(shù)組的特殊遍歷
  24.     observeArray(arr) {
  25.         for (let i = 0, l = arr.length; i < l; i++) {
  26.             // 逐項(xiàng)進(jìn)行observe
  27.             observe(arr[i]);
  28.         }
  29.     }
  30. }

二、依賴收集

目前對(duì)象中所有的屬性已經(jīng)變成可幀聽狀態(tài),下一步就進(jìn)入了依賴收集階段,其整個(gè)流程如下所示:

其實(shí)看了這張神圖后,由于能力有限還不是很理解,經(jīng)過自己的拆分,認(rèn)為可以分成兩個(gè)步驟去理解。

1.getter中(Object.defineProperty中的get屬性)進(jìn)行收集依賴后的狀態(tài)

2. 緊接著就是觸發(fā)依賴,該過程是在setter中進(jìn)行,當(dāng)觸發(fā)依賴時(shí)所存儲(chǔ)在Dep中的所有Watcher均會(huì)被通知并執(zhí)行,通知其關(guān)聯(lián)的組件更新,例如數(shù)據(jù)更新的位置是與Dep1所關(guān)聯(lián)的數(shù)據(jù),則其上的Watcher1、Watcher2、WatcherN均會(huì)被通知并執(zhí)行。

說了這么多,其中最核心的內(nèi)容無外乎Dep類、Watcher類、defineReactive函數(shù)中的set和get函數(shù)。

2.1 Dep類

Dep類用于管理依賴,包含依賴的添加、刪除、發(fā)送消息,是一個(gè)典型的觀察者模式。

 
 
 
 
  1. export default class Dep {
  2.     constructor() {
  3.         console.log('DEP構(gòu)造器');
  4.         // 數(shù)組存儲(chǔ)自己的訂閱者,這是Watcher實(shí)例
  5.         this.subs = [];
  6.     }
  7.     // 添加訂閱
  8.     addSub(sub) {
  9.         this.subs.push(sub);
  10.     }
  11.     // 添加依賴
  12.     depend() {
  13.         // Dep.target指定的全局的位置
  14.         if (Dep.target) {
  15.             this.addSub(Dep.target);
  16.         }
  17.     }
  18.     // 通知更新
  19.     notify() {
  20.         const subs = this.subs.slice();
  21.         for (let i = 0, l = subs.length; i < l; i++) {
  22.             subs[i].update();
  23.         }
  24.     }
  25. }

2.2 Watcher類

Watcher類的實(shí)例就是依賴,在其實(shí)例化階段會(huì)作為依賴存儲(chǔ)到Dep中,在對(duì)應(yīng)的數(shù)據(jù)改變時(shí)會(huì)更新與該數(shù)據(jù)相關(guān)的Watcher實(shí)例,進(jìn)行對(duì)應(yīng)任務(wù)的執(zhí)行,更新對(duì)應(yīng)組件。

 
 
 
 
  1. export default class Watcher {
  2.     constructor(target, expression, callback) {
  3.         console.log('Watcher構(gòu)造器');
  4.         this.target = target;
  5.         this.getter = parsePath(expression);
  6.         this.callback = callback;
  7.         this.value = this.get();
  8.     }
  9.     update() {
  10.         this.run();
  11.     }
  12.     get() {
  13.         // 進(jìn)入依賴收集階段,讓全局的Dep.target設(shè)置為Watcher本身,就進(jìn)入依賴收集階段
  14.         Dep.target = this;
  15.         const obj = this.target;
  16.         let value;
  17.         try {
  18.             value = this.getter(obj);
  19.         }
  20.         finally {
  21.             Dep.target = null;
  22.         }
  23.         return value;
  24.     }
  25.     run() {
  26.         this.getAndInvoke(this.callback);
  27.     }
  28.     getAndInvoke(cb) {
  29.         const value = this.get();
  30.         if (value !== this.value || typeof value === 'object') {
  31.             const oldValue = this.value;
  32.             this.value = value;
  33.             cb.call(this.target, value, oldValue);
  34.         }
  35.     }
  36. }
  37. function parsePath(str) {
  38.     const segments = str.split('.');
  39.     return obj =>{
  40.         for (let i = 0; i < segments.length; i++) {
  41.             if (!obj) {
  42.                 return;
  43.             }
  44.             obj = obj[segments[i]];
  45.         }
  46.         return obj;
  47.     };
  48. }

2.3 defineReactive函數(shù)中的set和get函數(shù)

Object.defineProperty中的getter階段進(jìn)行收集依賴,setter階段觸發(fā)依賴。

 
 
 
 
  1. export default function defineReactive(data, key, val) {
  2.     const dep = new Dep();
  3.     if (arguments.length === 2) {
  4.         val = data[key];
  5.     }
  6.     // 子元素要進(jìn)行observe,至此形成了遞歸
  7.     let childOb = observe(val);
  8.     Object.defineProperty(data, key, {
  9.         // 可枚舉
  10.         enumerable: true,
  11.         // 可配置
  12.         configurable: true,
  13.         // getter
  14.         get() {
  15.             console.log(`訪問${key}屬性`);
  16.             // 如果現(xiàn)在處于依賴收集階段
  17.             if (Dep.target) {
  18.                 dep.depend();
  19.                 // 其子元素存在的時(shí)候也要進(jìn)行依賴收集(個(gè)人認(rèn)為主要是針對(duì)數(shù)組)
  20.                 if (childOb) {
  21.                     childOb.dep.depend();
  22.                 }
  23.             }
  24.             return val;
  25.         },
  26.         // setter
  27.         set(newValue) {
  28.             console.log(`改變${key}的屬性為${newValue}`);
  29.             if (val === newValue) {
  30.                 return;
  31.             }
  32.             val = newValue;
  33.             // 當(dāng)設(shè)置了新值,這個(gè)新值也要被observe
  34.             childOb = observe(newValue);
  35.             // 發(fā)布訂閱模式,通知更新
  36.             dep.notify();
  37.         }
  38.     });
  39. }

名稱欄目:一文搞定數(shù)據(jù)響應(yīng)式原理
網(wǎng)站地址:http://m.5511xx.com/article/djjgcjd.html