1. 程式人生 > 其它 >openfeign攔截響應結果_Vue 2.x響應式原理剖析(一)

openfeign攔截響應結果_Vue 2.x響應式原理剖析(一)

技術標籤:openfeign攔截響應結果

前言

相信大家對於 Vue 的資料響應式原理並不陌生,vue2 中利用 Object.defineProperty() 實現變更檢測,而 Vue3 則利用了 ES6 提供的 ProxyAPI 來取代了之前的 defineProperty 來實現這一功能。既然知道其響應式原理,那麼我們該怎麼實現一個數據攔截方法呢?接下來,讓我們一步步來實現一個自己的 資料攔截庫吧!

基本概念

MVVM 框架

MVVM是 Model-View-ViewModel 的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的 View 的狀態和行為抽象化,讓我們將檢視 UI 和業務邏輯分開。當然這些事 ViewModel 已經幫我們做了,它可以取出 Model 的資料同時幫忙處理 View 中由於需要展示內容而涉及的業務邏輯。

MVVM 框架的三個要素:資料響應式、模版引擎及其渲染

  • 資料響應式:監聽資料變化並在試圖中更新(資料變更能夠響應在檢視中,就是資料響應式)

–Vue 2.x 版本:Object.defineProperty()

–Vue 3.x 版本:Proxy

  • 模版引擎:提供描述檢視的模版語法

–插值:{{}}

–指令:v-bind, v-on, v-model, v-for, v-if...

  • 渲染:如何將模版轉換為 html

–模版 => vnode => dom

實現資料偵測

1. 基礎方法定義

// reactive.jsfunction defineReactive (obj, key, val) {  Object.defineProperty(obj, key, {    get() {      // 每次取值時輸出日誌,方便除錯      console.log(`some data was get --- key: ${key}, val: ${val}`)      return val    },    set(newVal) {      if(newVal !== val) {        // 每次賦值時輸出日誌,方便除錯                 console.log(`new data was set --- key: ${key}, val: ${newVal}`)        val = newVal      }    }  })}

現在我們基本實現了一個最原始的資料攔截函式,不妨來測試一下

// reactive.jslet test = {}defineReactive(test, 'foo', 'firstblood')// 取 foo 的值test.foo// 設定 foo 的值test.foo = 'foo'

上面我們定義了一個物件 test,並用之前已寫好的 defineReactive 函式對其進行加工後嘗試取 foo 的值。

此時當我們執行 node reactive.js 後控制檯輸出結果

74e4fb466ae581e5b2762683b2a002ad.png

表明我們對 test 這個物件的取值和賦值已經成功攔截!

雖然目前這個簡易版本的 defineReactive 已經基本實現了物件攔截的操作,但仍有許多不足,譬如:

  • 需要我們手動處理物件的每一個屬性(key)

defineReactive(test,'foo','foo')defineReactive(test, 'bar', 'bar')defineReactive(test, 'baz', 'baz')
  • 當物件屬性也是一個物件的時候,無法繼續檢測物件屬性的屬性

let test = {  foo: {    id: 1,    name: 'foo',  }}defineReactive(test, 'foo', {name: 'newFoo'})test.foo.id// node 執行當前檔案後輸出 'some data was get --- key: foo, val: [object Object]'// 說明只有 foo 屬性成功被檢測,而 foo 的 id 屬性無法被檢測
  • 賦值為物件時,也無法繼續檢測

 let test = {}defineReactive(test, 'foo', {name: 'newFoo'})test.foo.name// node 執行當前檔案後輸出 'some data was get --- key: foo, val: [object Object]'// 說明只有 foo 屬性成功被檢測,而 foo 的 name 屬性無法被檢測
  • 如果物件新增/刪除了新屬性無法檢測

let test = {}defineReactive(test, 'foo', 'firstblood')// foo 取值test.foo// node 執行後輸出 'some data was get --- key: foo, val: firstblood'test.bar// node 執行後僅僅輸出 'some data was get --- key: foo, val: firstblood', 並未監測到 bar 取值

基於以上不足之處,我們需要繼續完善我們的物件攔截操作

2. 改造完善 defineReactive

  • 遍歷需要響應化的物件

 // reactive.js  function observe (obj) {    // 對傳入的引數做型別判斷    if (typeof obj !== 'object' || obj === null) return              // 物件響應化:遍歷每個key,定義getter、setter    Object.keys(obj).forEach(key => {               // 呼叫前面已經寫好的攔截方法      defineReactive(obj, key, obj[key])    })  }

通過 observe 方法, 我們物件每個屬性進行遍歷並對其設定了攔截操作,這樣我們只要將需要做攔截的物件交由 observe 處理一下,就可以實現物件的所有屬性自動攔截

const myData = {    foo: 'foo',    bar: 'bar',    baz: {      name: 'baz'    }  }  observe(myData)  // test  myData.foo  myData.bar = 'newBar'  myData.baz.name

node執行以上程式碼後控制檯輸出,證明目前物件屬性自動攔截功能已經基本實現, 但巢狀物件仍舊是有問題的

9e91c03eeee53812df9aff571e189f22.png

  • 解決巢狀物件問題

當物件的屬性值也為物件時,我們只需要物件的屬性值也交給 observe 處理一下就可以了

// reactive.js  function defineReactive(obj, key, val) {    observe(val)    Object.defineProperty(obj, key, {      //...    })      //...  }

測試看看:

 const myData = {    foo: 'foo',    bar: 'bar',    baz: {      name: 'baz'    }  }  observe(myData)    myData.baz.name

node 執行後控制檯輸出如下,說明我們實現了對巢狀物件資料存取偵測

d40cc63b641588cda0689a8901560509.png

  • 解決賦值是物件的問題

如果在給物件的某個屬性賦值時,值為物件,那麼我們需要對該屬性值也 observe 一下,使其也成為偵測物件

// reactive.js  function defineReactive(obj, key, val) {    observe(val)    Object.defineProperty(obj, key, {      get () {        // ...      },      set (newVal) {        // ...        observe(newVal) // 新值是物件的情況        // ...      }    })      //...  }
  • 解決 新增/刪除了新屬性問題

// reactive.js    // 新增一個set函式來處理  function set(obj, key, val) {    defineReactive(obj, key, val)  }

至此,我們就實現了一個簡易版的資料攔截庫!

完整版程式碼如下:

/** * 將物件轉化為響應式資料 * @param {*} obj 需要響應化的物件 * @param {*} key 屬性 * @param {*} val 值 */function defineReactive (obj, key, val) {  // 解決諸如 test.baz.a 物件巢狀問題  observe(val)  Object.defineProperty(obj, key, {    get() {      console.log(`get ${key}: ${val}`)      return val    },    set(newVal) {      if (newVal!==val) {        console.log(`set ${key}: ${newVal}`)        val = newVal        // 解決賦的值是物件的情況(譬如test.foo={f1: 666})        observe(val)      }    }  })}/** * 物件響應化:遍歷每個key,定義getter、setter * @param {*} data 需要響應化的物件 */function observe (data) {  if(typeof data !== 'object' || data === null) {    return  }  Object.keys(data).forEach(key=> {    defineReactive(data, key, data[key])  })}/** * 新增新屬性 * @param {*} obj  * @param {*} key  * @param {*} val  */function $set (obj, key, val){  defineReactive(obj, key, val)}

結語

今天我們已經基本實現了一個簡易版的資料攔截庫,那麼我們如何利用這個庫來實現資料響應化,使資料變化驅動檢視響應呢?Vue2.x 裡又是怎麼做的呢?篇幅有限,且聽下回分解吧~!