Vue 3.0 新特性 預覽

新特性總結

  • 全局API和內置組件/功能支持tree-shaking
  • 常駐代碼大小控制在10kb gzipped上下
  • 編譯器架構重構,更多的編譯時優化
  • 添加TypeScript支持,使外部貢獻者更有信心作改動
  • 代碼採用monorepo結構,內部分層更清晰

1、使Vue更快(性能優化)

基於Proxy的響應式實現,性能總體優於Object.defineProperty

長遠來看,JS引擎會繼續優化Proxy,但Object.defineProperty(getter/setter)基本不會再有針對性的優化html

Virtual DOM 重構

2.x版本的VDOM更新
  • Vue雖然可以保證觸發組件更新的最小化,但在單個組件內部依然須要遍歷該組件的整個VDOM樹
  • 但若是碰到下列只有少許動態節點時,這些遍歷都是性能的浪費
  • 所以:傳統的VDOM的性能與模板帶下正相關,與動態節點數目無關
3.x 動靜分離優化,提示更新效率

React 解決方法: 時間分片vue

  • 經過模板靜態分享生成更優化的VDOM渲染函數node

  • 先將模板切分爲block,每一個block內部動態節點位置都是固定的npm

  • 每一個block的根節點會記錄本身所包含的動態節點(包含子block的根節點)編程

  • 更新時,只須要直接遍歷動態節點api

  • block Tree 數組

  • 最簡單狀況 性能優化

  • v-if狀況 架構

  • 優化效果app

    • 新策略將VDOM更新性能與模板大小解耦,變爲與動態節點數目相關
    • 總體性能提高2~5倍

2、Function-based API

單個函數內部的變量會被壓縮,對壓縮更友好

替換Class API

  • class api(vue-property-derocator和vue-class-component) 對vue而已只是擴展了對TypeScript和與React相似的裝飾器寫法,其他並無其餘的優勢,
  • Derocator提案嚴重不穩定是的依賴他的方案具備很大風險
  • Props和其餘須要注入到this的屬性致使類型聲明存在很大問題

邏輯複用

  • Mixin
    • 混入的屬性來源不清楚
    • 命名空間衝突
  • 高階組件(HOC)
    • Props來源不清楚
    • Props命名空間衝突
    • 多餘的組件實例形成性能浪費
  • Scoped Slots
    • 來源清楚
    • 無命名空間衝突
    • 多餘的組件形成性能浪費
  • Composition Functions (Function-based API提供)
    • 即函數組合
    • 無額外實例開銷

優勢

  • 更靈活的邏輯複用能力
  • 更好的TypeScript類型推導支持
  • 更好的性能
  • Tree-shaking優化
    • 基於函數的 API 每個函數均可以做爲 named ES export 被單獨引入,這使得它們對 tree-shaking 很是友好。沒有被使用的 API 的相關代碼能夠在最終打包時被移除
  • 代碼容易壓縮
    • 全部的函數名和 setup 函數體內部的變量名均可以被壓縮,但對象和 class 的屬性/方法名卻不能夠

使用Function-based API

官方提供了一個vue-function-api依賴庫,以便vue2.x也可使用function api編程,不過它只有Function-based API的setup、value、computed、watch、lifecycle、provide、inject等部分API

// npm install vue-function-api --save
// 在2.x中使用
import Vue from 'vue'
import { plugin } from 'vue-function-api'
Vue.use(plugin)
複製代碼
setup函數

是Function-Based API的入口函數,相似data(),setup()的返回值就是注入頁面模板的變量,能夠像data同樣直接使用

import { value } from 'vue'
function useMousePosition() { // 組件內容複用 Composition Function
const x = value(0) const y = value(0) const update = e => {
        x.value = e.pageX
       y.value = e.pageY
    }
    onMounted(() => {
       window.addEventListener('mousemove', update)
    })
    onUnmounted(() => {
       window.removeEventListener('mousemove', update)
    })
    return { x, y }
}
const MyComponent = {
 setup(props) {
   const msg = value('hello')
   const {x, y} = useMousePosition()
   const appendName = () => {
     msg.value = `hello ${props.name}`
   }
   return {
     msg,
     appendName
   }
 },
 template: `<div @click="appendName">{{ msg }}</div>`
}
複製代碼
  • 函數API配合Render實現
    • 若是你的組件不使用模版,你也能夠選擇在 setup() 中直接返回一個渲染函數
import { value, createElement as h } from 'vue'
const MyComponent = {
  setup(initialProps) {
    const count = value(0)
    const increment = () => { count.value++ }

    return (props, slots, attrs, vnode) => (
      h('button', {
        onClick: increment
      }, count.value)
    )
  }
} 
複製代碼
value()返回的是一個value wrapper(包裝對象)
  • 一個包裝對象只有一個屬性.value,指向內部被包裝的值,該值能夠被修改

  • 爲何要使用

    • 咱們知道在 JavaScript 中,原始值類型如 string 和 number 是隻有值,沒有引用的。若是在一個函數中返回一個字符串變量,接收到這個字符串的代碼只會得到一個值,是沒法追蹤原始變量後續的變化的。
    • 所以,包裝對象的意義就在於提供一個讓咱們可以在函數之間以引用的方式傳遞任意類型值的容器
  • Value Unwrapping(包裝對象的自動展開)

    • 當包裝對象被暴露給模版渲染上下文,或是被嵌套在另外一個響應式對象中的時候,它會被自動展開 (unwrap) 爲內部的值
const MyComponent = {
  setup() {
    return {
      count: value(0)
    }
  },
  template: `<button @click="count++">{{ count }}</button>`
}
複製代碼
生命週期函數

全部現有的生命週期鉤子都會有對應的 onXXX 函數(只能在 setup() 中使用):

import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    // destroyed 調整爲 unmounted
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
複製代碼
Computed Value (計算值)

計算值的行爲跟計算屬性 (computed property) 同樣:只有當依賴變化的時候它纔會被從新計算。 computed(getter, setter) 返回的是一個只讀的包裝對象,它能夠和普通的包裝對象同樣在 setup() 中被返回 ,也同樣會在渲染上下文中被自動展開。默認狀況下,若是用戶試圖去修改一個只讀包裝對象,會觸發警告

import { value, computed } from 'vue'
const count = value(0)
const countPlusOne = computed(() => count.value + 1)
console.log(countPlusOne.value) // 1
count.value++
console.log(countPlusOne.value) // 2
複製代碼
Watchers
  • watch() API 提供了基於觀察狀態的變化來執行反作用的能力。
  • watch() 接收的第一個參數被稱做 「數據源」,它能夠是:
    • 一個返回任意值的函數
    • 一個包裝對象
    • 一個包含上述兩種數據源的數組
  • 第二個參數是回調函數。回調函數只有當數據源發生變更時纔會被觸發
  • 觀察的對象能夠是:Props,包裝對象等
// double 是一個計算包裝對象
const double = computed(() => count.value * 2)
watch(double, value => {
  console.log('double the count is: ', value)
}) // -> double the count is: 0
count.value++ // -> double the count is: 2
複製代碼
  • 中止觀察
const stop = watch(...)
// stop watching
stop()

// 若是 watch() 是在一個組件的 setup() 或是生命週期函數中被調用的,那麼該 watcher 會在當前組件被銷燬時也一同被自動中止:
export default {
  setup() {
    // 組件銷燬時也會被自動中止
    watch(/* ... */)
  }
}
複製代碼
純 TypeScript 的 Props 類型聲明

3.0 的 props 選項不是必須的,若是你不須要運行時的 props 類型檢查,你也能夠選擇徹底在 TypeScript 的類型層面聲明 props 的類型:

import { createComponent, createElement as h } from 'vue'
interface Props {
  msg: string
}
const MyComponent = createComponent({
  setup(props: Props) {
    return () => h('div', props.msg)
  }
})
複製代碼
類型推導

createComponent 從概念上來講和 2.x 的 Vue.extend 是同樣的,但在 3.0 中它實際上是單純爲了類型推導而存在的,內部實現是個 noop(直接返回參數自己)。它的返回類型能夠用於 TSX 和 Vetur 的模版自動補全。若是你使用單文件組件,則 Vetur 能夠自動隱式地幫你添加這個調用

import { createComponent } from 'vue'
const MyComponent = createComponent({
  // props declarations are used to infer prop types
  props: {
    msg: String
  },
  setup(props) {
    props.msg // string | undefined

    // bindings returned from setup() can be used for type inference
    // in templates
    const count = value(0)
    return {
      count
    }
  }
})
複製代碼

參考文獻

相關文章
相關標籤/搜索