Vue3 新特性

黃大渣渣發表於2020-06-11

一、vue3 為什麼要重寫

  兩個主要原因考慮重寫vue新版本主要功能:

  1.主流瀏覽器對新的JavaScript語言特性的普遍支援。

  2.當前Vue程式碼庫隨著時間的推移而暴露出來的設計和體系架構問題。

  3.對一些方法及API進行優化

  以下是一些原理上的分析:

  1.瀏覽器效能提升

  首先,隨著ES6的發展已及廣泛使用,瀏覽器對這些新的特性逐漸增加,效能不斷優化,這就給vue3優化提供了一個機會,通過重寫來優化提升vue的效能。

  2.底層實現方法

  其次,在框架設計上,vue2.0 是採用Object.defineProperty來實現雙向繫結原理,這個屬性本身就存在一些不足的地方,比如:

  1.Object.defineProperty無法監控到陣列下標的變化,導致直接通過陣列的下標給陣列設定值,不能實時響應。 為了解決這個問題,經過vue內部處理後可以使用以下幾種方法來監聽陣列,push(),pop(),shift(),unshift(),splice(),sort(),reverse();由於只針對了以上八種方法進行了hack處理,所以其他陣列的屬性也是檢測不到的,還是具有一定的侷限性。

  2.Object.defineProperty只能劫持物件的屬性,因此我們需要對每個物件的每個屬性進行遍歷。Vue 2.x裡,是通過 遞迴 + 遍歷 data 物件來實現對資料的監控的,如果屬性值也是物件那麼需要深度遍歷,顯然如果能劫持一個完整的物件是才是更好的選擇,新增的屬性還行通過set方法來新增監聽,有一定的侷限性。

  vue3主要採用的Proxy特性,相比之下有以下優點:

  1.可以劫持整個物件,並返回一個新的物件

  2.有13種劫持操作

  但同時Proxy作為ES6的新特性,有一定的相容問題,最主要的是這個屬性無法用polyfill來相容,這個需要在vue3中需要解決的問題。

  3.切換到TypeScript 

  Vue 2最初是用純ES(Javascript)寫成的。在原型設計階段之後不久,我們意識到一個型別系統(Type system)對於這樣一個規模的專案非常有用。型別檢查(Type check)大大減少了在重構過程中引入意外錯誤的機會,並幫助貢獻者更有信心進行大範圍的更改。我們採用了Facebook的Flow type checker,因為它可以逐漸新增到現有的純ES專案中。Flow type checker在一定程度上起到了幫助作用,但我們並沒有從中得到我們所希望的那麼多好處。特別是,持續的重大改變使得升級成為一種痛苦。相比較TypeScript與Visual Studio Code整合開發工具的深度整合,Flow type checker對整合開發環境的支援也不理想。

  我們還注意到,使用者越來越多地同時使用Vue和TypeScript。為了支援它們的用例,我們必須獨立於使用不同型別系統的原始碼來編寫和維護TypeScript宣告。切換到TypeScript將允許我們自動生成宣告檔案,從而減輕維護負擔。

  效能對前端框架至關重要。儘管Vue 2號稱具有良好的效能,但重寫提供了一個機會,可以通過試驗新的渲染策略來更提供更好的效能。

  4.克服虛擬DOM的瓶頸 

  Vue有一個相當獨特的渲染策略:它提供類似於HTML的模板語法,但是,它是將模板編譯成渲染函式來返回虛擬DOM樹。Vue框架通過遞迴遍歷兩個虛擬DOM樹,並比較每個節點上的每個屬性,來確定實際DOM的哪些部分需要更新。由於現代JavaScript引擎執行的高階優化,這種有點暴力的演算法通常非常快速,但是DOM的更新仍然涉及許多不必要的CPU工作。當你看到一個基本上是靜態內容、只有少量動態繫結的模板時,效率低下的情況尤其明顯,因為這時候仍然需要遞迴地遍歷整個虛擬DOM樹,以找出需要更改的內容。

  幸運的是,模板編譯步驟使我們有機會對模板執行靜態分析並提取有關動態部分的資訊。Vue 2在某種程度上是通過跳過靜態子樹來實現的,但是過於簡單的編譯器體系架構使得更高階的優化很難實現。在Vue 3中,我們使用適當的AST轉換管道重寫編譯器,這允許我們以轉換外掛的形式將編譯時(compile-time)優化組合進來。

  隨著新的體系架構的出現,我們希望找到一種能夠儘可能減少開銷的渲染策略。一種選擇是拋棄虛擬DOM並直接生成命令式DOM操作,但這樣做會消除直接編寫虛擬DOM渲染函式的能力,而我們發現這種能力對於高階使用者和庫的編寫者非常有價值。另外,這將是一個巨大的突破性改變。另一個更好的辦法是去掉不必要的虛擬DOM樹遍歷和屬性比較,這在更新期間往往會產生最大的效能開銷。為了實現這一點,編譯器和執行時需要協同工作:編譯器分析模板並生成帶有優化提示的程式碼,而執行時儘可能獲取提示並採用快速路徑。這裡有三個主要的優化:

  首先,在DOM樹級別。我們注意到,在沒有動態改變節點結構的模板指令(例如v-if和v-for)的情況下,節點結構保持完全靜態。如果我們將一個模板分成由這些結構指令分隔的巢狀“塊”,則每個塊中的節點結構將再次完全靜態。當我們更新塊中的節點時,我們不再需要遞迴遍歷DOM樹 - 該塊內的動態繫結可以在一個平面陣列中跟蹤。這種優化通過將需要執行的樹遍歷量減少一個數量級來規避虛擬DOM的大部分開銷。

  其次,編譯器積極地檢測模板中的靜態節點、子樹甚至資料物件,並在生成的程式碼中將它們提升到渲染函式之外。這樣可以避免在每次渲染時重新建立這些物件,從而大大提高記憶體使用率並減少垃圾回收的頻率。

  第三,在元素級別。編譯器還根據需要執行的更新型別,為每個具有動態繫結的元素生成一個優化標誌。例如,具有動態類繫結和許多靜態屬性的元素將收到一個標誌,提示只需要進行類檢查。執行時將獲取這些提示並採用專用的快速路徑。

綜合起來,這些技術大大改進了我們的渲染更新基準,Vue 3有時佔用的CPU時間不到Vue 2的十分之一。

  4.體積變小

  vue向來被稱為輕便巧,重寫後框架只有10K左右,比原來體積少了一半,並且優化了打包方法,使得打包後的bundle的體積更小。

二、vue3 一些重點API更新情況

  1.插槽優化

  在當前的 Vue 版本中,當父元件重新渲染時,其子元件也必須重新渲染。 使用 Vue 3 ,可以單獨重新渲染父元件和子元件。

  2.虛擬dom優化

  隨著虛擬 DOM 重寫,我們可以期待更多的 編譯時(compile-time)提示來減少 執行時(runtime)開銷。重寫將包括更有效的程式碼來建立虛擬節點。

  3.靜態樹提升

  使用靜態樹提升,這意味著 Vue 3 的編譯器將能夠檢測到什麼是靜態元件,然後將其提升,從而降低了渲染成本。它將能夠跳過未整個樹結構打補丁的過程。

  4.靜態屬性提升

  此外,我們可以期待靜態屬性提升,其中 Vue 3 將跳過不會改變節點的打補丁過程。

  5.基於Proxy的觀察者機制

  6. Hooks API (Composition API)

  組合API是Vue的下一個主要版本中最常用的討論和特色語法。這是一種全新的邏輯重用和程式碼組織方法。當前,我們使用所謂的Options API構建元件。現在,新增到Vue元件的邏輯通常會採用:如datamethodscomputed等這種方法,最大缺點是這是不JavaScript程式碼原生方式。您需要確切瞭解模板中可以訪問哪些屬性,以及this關鍵字的行為。在後臺,Vue編譯器需要將此屬性轉換為工作程式碼。因此,我們無法通過從自動建議或型別檢查中受益(而大家想一想Typescript,馬上就會明白了)。Composition API旨在,通過將元件屬性中當前可用的機制公開為JavaScript函式來解決此問題。Vue核心團隊將Composition API描述為“一組基於功能的附加API,可以靈活地組合元件邏輯”。用Composition API編寫的程式碼更具可讀性,並且都是原生JS,這使它更易於閱讀和學習。

  讓我們來看一個使用新的Composition API理解其工作原理的元件的簡單示例。

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}, click to increment.
  </button>
</template>
 
<script>
import { ref, computed, onMounted } from 'vue'
 
export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)
 
    function increment() {
      count.value++
    }
 
    onMounted(() => console.log('component mounted!'))
 
    return {
      count,
      double,
      increment
    }
  }
}
</script>

  mixins的最大缺點是:我們對它實際上新增到我們的元件中一無所知。這不僅使推理變得困難,而且還可能導致名稱與現有屬性和功能發生衝突。

  使用Composition API來共享程式碼:

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }
 
  return {
    count,
    incrememt
  }
}
 
export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

  我們不受模板和元件範圍的限制,並且確切地知道可以從計數器訪問哪些屬性。另外,我們可以從編輯器中可用的程式碼完成中受益,因為useCounter它只是一個返回某些屬性的函式。幕後沒有魔力,因此編輯器可以幫助我們進行型別檢查和建議。

  7.全域性安裝/配置API更改

  8.Fragments

  ......

 

 

本文參考 公眾號 前端大全

慕課網 Brian  Vue3中令人興奮的新功能