前端必讀:Vue響應式系統大PK

轉載請註明出處:葡萄城官網,葡萄城爲開發者提供專業的開發工具、解決方案和服務,賦能開發者。
原文參考: https://www.sitepoint.com/vue...

響應式系統(Reactivity systems)是現代前端框架的關鍵部分之一。應用系統的的高度交互性、動態性和響應能力全靠它支持。每一個Web開發人員而言都應該瞭解這一系統的功能和實踐操做。前端

原理

響應系統是一種使自動使數據源(模型)與數據表示(視圖)層自動保持同步的機制。每次模型更改時,都會從新渲染視圖。vue

以一個簡單的Markdown編輯器爲例。一般編輯器有兩個窗格:一個窗格用於編寫Markdown代碼(用於修改基礎模型),另外一個窗格用於預覽已編譯的HTML(顯示已更新的視圖)。當咱們在書寫窗格中寫東西時,它會當即在預覽窗格中自動預覽。這個例子比較簡單,在實際狀況中會複雜不少。react

在許多狀況下,咱們要顯示的數據取決於其餘數據。在這種狀況下,須要跟蹤相關數據,並根據跟蹤狀況來更新數據。例如,咱們有一個fullName,該屬性由firstName和lastName屬性組成。修改其任何依賴項後,fullName將自動從新評估,並在視圖中顯示結果。數組

瞭解什麼是響應式系統後,在瞭解Vue 3中的響應系統如何工做以及如何在實踐中使用以前,讓咱們一塊兒來快速回顧一下Vue 2中的響應系統內容及其注意事項。瀏覽器

Vue 2的響應式系統簡介

Vue 2中的響應或多或少會被「隱藏」。不管咱們放置在data對象中的是什麼,Vue都會使其隱式反應(reactive implicitly)。這樣雖然可使開發人員的工做更加輕鬆,但靈活度卻會不可避免的下降。
在幕後,Vue 2使用ES5 Object.defineProperty將data對象的全部屬性轉換爲getter和setter。對於每一個組件實例,Vue建立一個依賴關係觀察程序實例,觀察者會記錄組件渲染期間依賴收集/跟蹤的任何屬性。當屬性觸發依賴項的設置器時,將通知觀察者,並將組件從新渲染並更新視圖。可是卻也會有一些問題存在。前端框架

變動檢測警告

因爲Object.defineProperty方法的限制,Vue沒法檢測到某些數據更改。包括:app

  • 給對象添加屬性或把對象移除屬性(例如obj.newKey = value)
  • 按索引設置數組項(例如arr[index] = newValue)
  • 修改數組的長度(例如arr.length = newLength)
    不過爲了解決這些問題, Vue爲提供了Vue.set API方法,該方法向響應對象添加了一個屬性,確保新屬性也是響應性的,從而觸發了視圖更新。

用下述實例討論該狀況:框架

<h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1>
  <button @click="addAgeProperty">Add "age" property</button>
  <p>Here are my favorite activities:</p>
  <ul>
    <li v-for="item, index in activities" :key="index">
      {{ item }}
      <button @click="editActivity(index)">Edit</button>
    </li>
  </ul>
  <button @click="clearActivities">Clear the activities list</button>
</div>
el: '#app',
  data: {
    person: {
      name: "David"
    },
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Add a new property to an object
    addAgeProperty() {
      this.person.age = 30
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.length = 0 
    }
  }
});

在上面的示例中,咱們會發現這三種方法都不起做用。咱們不能向該person對象添加新屬性,沒法使用activities的索引來編輯數組中的項目,也不能修改activities數組的長度。編輯器

優化以下:函數

el: '#app',
  data: {
    person: {
      name: "David"
    },
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {
      Vue.set(this.person, 'age', 30)
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        Vue.set(this.activities, index, newValue)
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.splice(0)
    }
  }
});

在此示例中,咱們用Vue.setAPI方法將新age屬性添加到person對象,並從活動數組中選擇/修改特定項目。在最後一種狀況下,使用JavaScript內置splice方法。

這個作法徹底可行但卻略顯笨拙,並且會致使先後代碼不一致。而Vue 3就解決了這個問題。
咱們用下面示例繼續看:

data() {
    return {
      person: {
        name: "David"
      },
      activities: [
        "Reading books",
        "Listening music",
        "Watching TV"
      ]
    }
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {
      this.person.age = 30
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.length = 0 
    }
  }
}

Vue.createApp(App).mount('#app')

能夠看到在Vue 3中,全部方法均可以正常工做。

在Vue 2.6中,引入的Vue.observable API方法,必定程度的公開了響應式系統,使開發人員能夠體驗到響應式系統的內容。實際上,這和Vue內部用來包裝data對象是徹底相同的方法,對於在簡單場景建立小的跨組件狀態存儲頗有用。但依舊沒辦法和Vue3的響應式系統相比,接下來就爲你們詳細介紹。

注意:因爲Object.defineProperty方法是僅限ES5且不可調整的功能,所以Vue 2不支持IE8及如下版本。

Vue 3響應式系統如何工做

爲了充分利用ES6 Proxy and Reflect API ,Vue 3中的響應式系統已被徹底重寫。新版本新增響應式API,該API使系統比之前更加靈活和強大。

Proxy API容許開發人員攔截和修改目標對象上的更低級對象操做。代理(proxy)是對象的克隆/包裝(clone/wrapper),並提供特殊功能(稱爲target),這些功能響應特定的操做並覆蓋JavaScript對象的內置行爲(稱爲traps)。若是仍然須要使用默認行爲,則可使用相應的Reflection API,其名稱顧名思義就是反映Proxy API的方法。這裏有一個示例,用來了解如何在Vue 3中使用這些API:

name: "David",
  age: 27
};

const handler = {
  get(target, property, receiver) {
    // track(target, property)
    console.log(property) // output: name
    return Reflect.get(target, property, receiver)
  },
  set(target, property, value, receiver) {
    // trigger(target, property)
    console.log(`${property}: ${value}`) // output: "age: 30" and "hobby: Programming"
    return Reflect.set(target, property, value, receiver)
  }
}

let proxy = new Proxy(person, handler);   

console.log(person)

// get (reading a property value)
console.log(proxy.name)  // output: David

// set (writing to a property)
proxy.age = 30;

// set (creating a new property)
proxy.hobby = "Programming";

console.log(person)

要建立一個新的代理,使用new Proxy(target, handler)構造函數。它帶有兩個參數:目標對象(person對象)和處理程序對象,該對象定義將攔截哪些操做(get和set操做)。在handler對象中, get和set陷阱來跟蹤什麼時候讀取屬性以及什麼時候修改/添加屬性。設置控制檯語句以確保運行正確。

在get和set陷阱採用下列參數:

  • target:代理包裝的目標對象
  • property:屬性名稱
  • value:屬性值(此參數僅用於設置操做)
  • receiver:進行操做的對象(一般是代理)
  • Reflect API方法與其相應的代理方法接受相同的參數

註釋中track函數和trigger函數特定用於Vue,用於跟蹤什麼時候讀取屬性以及什麼時候修改/添加屬性。

在示例的最後一部分,用控制檯語句輸出原始person對象。而後用另外一份聲明中讀取屬性name的proxy對象。接下來,修改age屬性並建立一個新hobby屬性。最後,再次輸出該對象以查看它是否正確更新。

以上就是Vue3響應式系統的完整工做流程,但在實際工做中會複雜得多。

使用Vue 3響應式系統,還有一些注意事項:

  • 僅適用於支持ES6 +的瀏覽器
  • 響應代理不等於原始對象

總結

以上咱們將Vue2和Vue3中響應式系統部分進行了比較,並對響應式系統的工做原理進行了說明,在後面的文章中,咱們會進一步爲你們介紹Vue3中響應式系統的API,敬請期待。

拓展閱讀

若是你已經熟練掌握Vue3, 不妨讓咱們經過實際搭建一款基於Vue 3組件的表格編輯系統,更加深刻學習

相關文章
相關標籤/搜索