自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文搞定數(shù)據(jù)響應(yīng)式原理

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

[[420542]]

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

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

能夠幀聽(tīng)到數(shù)據(jù)變化是數(shù)據(jù)響應(yīng)式原理的前提,因?yàn)閿?shù)據(jù)響應(yīng)式正是基于監(jiān)聽(tīng)到數(shù)據(jù)變化后來(lái)觸發(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ì)想到通過(guò)遞歸的方式實(shí)現(xiàn),在Vue中為了保證代碼較高的可讀性,引入了三個(gè)模塊實(shí)現(xiàn)該邏輯:observe、Observer、defineReactive,其調(diào)用關(guān)系如下所示:

1.1.1 observe

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

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

1.1.2 Observer

這個(gè)函數(shù)的目的主要有兩個(gè):一個(gè)是將該實(shí)例掛載到該對(duì)象value的__ob__屬性上(observe上用到了該屬性,通過(guò)判斷是否有該屬性判斷是否已經(jīng)屬于幀聽(tīng)狀態(tài));另一個(gè)是遍歷該對(duì)象上的所有屬性,然后將該屬性均變?yōu)榭蓭?tīng)的(通過(guò)調(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.  
  12.     // 對(duì)于對(duì)象上的屬性進(jìn)行遍歷,將其變?yōu)轫憫?yīng)式的 
  13.     walk(value) { 
  14.         for (let key in value) { 
  15.             defineReactive(value, key); 
  16.         } 
  17.     } 

1.1.3 defineReactive

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

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

1.2 數(shù)組

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

除了改裝其原有數(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.  
  18.     // 對(duì)于對(duì)象上的屬性進(jìn)行遍歷,將其變?yōu)轫憫?yīng)式的 
  19.     walk(value) { 
  20.         for (let key in value) { 
  21.             defineReactive(value, key); 
  22.         } 
  23.     } 
  24.  
  25.     // 數(shù)組的特殊遍歷 
  26.     observeArray(arr) { 
  27.         for (let i = 0, l = arr.length; i < l; i++) { 
  28.             // 逐項(xiàng)進(jìn)行observe 
  29.             observe(arr[i]); 
  30.         } 
  31.     } 

二、依賴收集

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

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

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

2. 緊接著就是觸發(fā)依賴,該過(guò)程是在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í)行。

說(shuō)了這么多,其中最核心的內(nèi)容無(wú)外乎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.     // 添加訂閱 
  9.     addSub(sub) { 
  10.         this.subs.push(sub); 
  11.     } 
  12.  
  13.     // 添加依賴 
  14.     depend() { 
  15.         // Dep.target指定的全局的位置 
  16.         if (Dep.target) { 
  17.             this.addSub(Dep.target); 
  18.         } 
  19.     } 
  20.  
  21.     // 通知更新 
  22.     notify() { 
  23.         const subs = this.subs.slice(); 
  24.         for (let i = 0, l = subs.length; i < l; i++) { 
  25.             subs[i].update(); 
  26.         } 
  27.     } 

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.  
  10.     update() { 
  11.         this.run(); 
  12.     } 
  13.  
  14.     get() { 
  15.         // 進(jìn)入依賴收集階段,讓全局的Dep.target設(shè)置為Watcher本身,就進(jìn)入依賴收集階段 
  16.         Dep.target = this; 
  17.         const obj = this.target; 
  18.         let value; 
  19.  
  20.         try { 
  21.             value = this.getter(obj); 
  22.         } 
  23.         finally { 
  24.             Dep.target = null
  25.         } 
  26.  
  27.         return value; 
  28.     } 
  29.  
  30.     run() { 
  31.         this.getAndInvoke(this.callback); 
  32.     } 
  33.  
  34.     getAndInvoke(cb) { 
  35.         const value = this.get(); 
  36.  
  37.         if (value !== this.value || typeof value === 'object') { 
  38.             const oldValue = this.value; 
  39.             this.value = value; 
  40.             cb.call(this.target, value, oldValue); 
  41.         } 
  42.     } 
  43.  
  44. function parsePath(str) { 
  45.     const segments = str.split('.'); 
  46.  
  47.     return obj =>{ 
  48.         for (let i = 0; i < segments.length; i++) { 
  49.             if (!obj) { 
  50.                 return
  51.             } 
  52.             obj = obj[segments[i]]; 
  53.         } 
  54.  
  55.         return obj; 
  56.     }; 

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

 

責(zé)任編輯:姜華 來(lái)源: 前端點(diǎn)線面
相關(guān)推薦

2024-01-09 08:24:47

JMM核心線程

2022-07-15 08:16:56

Stream函數(shù)式編程

2020-05-13 09:14:16

哈希表數(shù)據(jù)結(jié)構(gòu)

2021-10-06 20:23:08

Linux共享內(nèi)存

2024-03-26 00:33:59

JVM內(nèi)存對(duì)象

2021-08-13 05:50:01

ContainerdDockerKubernetes

2021-10-25 16:01:01

Linux設(shè)備樹(shù)字符串

2021-03-28 18:40:02

LinuxWindowsJava

2020-10-27 10:26:03

編程開(kāi)發(fā)Java

2019-09-23 10:51:14

JavaJava虛擬機(jī)Linux

2022-08-17 18:25:37

Java分布式搜索引擎

2021-08-31 07:02:20

Diff算法DOM

2023-09-08 08:20:46

ThreadLoca多線程工具

2020-10-29 08:55:04

微服務(wù)

2022-04-15 08:03:41

SaaS應(yīng)用管理市場(chǎng)

2021-04-19 17:32:34

Java內(nèi)存模型

2021-04-02 06:17:10

大數(shù)加減乘除數(shù)據(jù)結(jié)構(gòu)算法

2025-04-07 08:20:00

ORMPython代碼

2021-01-13 05:21:59

參數(shù)

2018-05-13 16:06:55

數(shù)據(jù)科學(xué)機(jī)器學(xué)習(xí)面試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)