關於vue3你須要知道的事

1. 寫在前面的話

vue3在今年內應該就會推出,尤大以前也針對vue3的設計和api給出了RFC和最新徵求意見稿。本人閱讀後總結了一些我的心得,但願對你們有用。javascript

2. Vue3 Function-based API RFC

vue2在近幾年不太長的生命週期內,經歷了大小各類項目的歷練,能夠說很是易於上手,方便開發。同時也暴露了很多問題亟待解決:css

  1. 組件之間難以邏輯組合與複用
  2. vue2缺乏類型推導,即typescript支持
  3. 打包尺寸較大,vue2打包時會將全部api的核心代碼打包,不論其在開發中是否有用到
  4. definePropterty()實現的數據綁定沒法實現數據對象新增屬性值的變化,且vue中的數組方法也是本身polyfill實現的

vue3基於上述弊端從新設計了內部的實現方式,而且同時從新設計了組件選項和api。主要有:html

從新設計了響應式數據對象,內部實現從defineProperty改成由瀏覽器原生支持的proxy實現。包括手動定義響應式數據(reactive)和包裝對象(ref)兩種api方式建立響應式數據。前端

  • 將數據變爲響應式數據,能夠追蹤數據的變化,包括數組和對象的屬性變化
  • 包裝對象數據以變量的形式引用時(使用/修改)須要以a.value這種方式

示例代碼:vue

// reactive響應式數據
import { reactive } from 'vue'
const state = reactive({
  count: 0
})

function increment() {
  state.count ++
}
複製代碼
// ref包裝對象
import { ref } from 'vue'
const count = ref(0);

// 以變量的形式引用包裝對象
function increment() {
  count.value ++
}
複製代碼

新增了watchEffect api,和vue2中的watch效果相似,監測響應式數據變化並執行反作用函數。java

  • watchEffect將會在建立的時候當即執行一次,以後會在renderer flush即DOM更新後再執行回調函數,組件銷燬時watch中止觀察。
  • watchEffect不須要將被依賴的數據源和反作用回調函數分開,只需寫出反作用函數,便可自動將反作用中的全部響應式狀態的 property 做爲依賴進行追蹤
import { reactive, ref, watchEffect } from 'vue'

const state = reactive({
  count: 0,
})
const titleCount = ref(0);

// 當state.count、titleCount變化時,watchEffect將會自動執行反作用回調函數
watchEffect(() => {
  document.body.innerHTML = `count is ${state.count}`
  document.title = `title count is ${titleCount}`
});
複製代碼

從新設計了computed api,將會建立一個依賴其它狀態的響應式狀態。被依賴的狀態能夠是由reactive或ref建立響應式數據,生成的狀態則是一個只讀的包裝對象,以變量的形式使用時須要以.value這種方式。react

import { reactive, ref, computed } from 'vue'

const state = reactive({
  count: 0,
})
const num = ref(0);

const double = computed(() => state.count * 2)
// 使用ref建立響應式對象時須要用.value取值計算
const triple = computed(() => num.value * 3)

watchEffect(() => {
  console.log(double.value) // -> 0
  console.log(triple.value) // -> 0
})

state.count++ // -> 2
num.value++ // -> 3
複製代碼

新設計了setup()組件選項,代替以前的data組件選項。這個函數是組件開始邏輯的地方,它接收組件的props屬性做爲參數,而後開始調用,整個組件生命週期內只會執行一次。webpack

  • 在setup中建立響應式數據對象,並return出去暴露給模板的上下文使用
  • setup能夠將函數方法返回出去,給模板使用
  • setup接收的組件屬性props自己也是個可追蹤的響應式數據
  • vue2中的生命週期通過重命名,只在setup中執行
import { ref } from 'vue'

const MyComponent = {
  setup(props) {
    const msg = ref('hello')
    const appendName = () => {
      msg.value = `hello ${props.name}`
    }
    return {
      msg,
      appendName
    }
  },
  template: `<div @click="appendName">{{ msg }}</div>`
}
複製代碼

類型推導,須要使用defineComponent函數定義組件才能支持typescript類型推導web

import { defineComponent, ref } from 'vue'

const MyComponent = defineComponent({
  // props選項聲明用來定義prop屬性類型
  props: {
    msg: String
  },
  setup(props) {
    console.log(props.msg) // string 或 undefined

    // setup中返回的數據對象能夠在模板中用於類型推斷推斷類型
    const count = ref(0)
    return {
      count
    }
  }
})
複製代碼

在瞭解了上面的一些基本api後,咱們能夠看一下尤大給出的一個基本組件寫法:typescript

import { ref, computed, watchEffect, onMounted } from 'vue'

const App = {
  template: ` <div> <span>count is {{ count }}</span> <span>plusOne is {{ plusOne }}</span> <button @click="increment">count++</button> </div> `,
  setup() {
    // reactive state
    const count = ref(0)
    // computed state
    const plusOne = computed(() => count.value + 1)
    // method
    const increment = () => { count.value++ }
    // watch
    watchEffect(() => count.value * 2, val => {
      console.log(`count * 2 is ${val}`)
    })
    // lifecycle
    onMounted(() => {
      console.log(`mounted`)
    })
    // expose bindings on render context
    return {
      count,
      plusOne,
      increment
    }
  }
}
複製代碼

這裏只介紹了少數比較重要的vue3 api,並且不肯定最終api還會不會再改動,若是想要詳細查詢學習的同窗能夠去查看官方api文檔

3. vue3對比react hooks

尤大親自說過vue3的最新設計很大程度上是借鑑了react hooks的思路。具體類似的地方至少有:

  1. 定義數據的寫法方式相似,react使用useState,vue3使用ref,建立響應式數據
  2. setup設計借鑑了react hooks,抽取函數和複用邏輯的方式相似,將邏輯和數據抽取成爲獨立的函數進行復用。

官方文檔也給出了一個將獲取鼠標位置的功能抽取成獨立函數的實例:

// 抽取獲取鼠標x, y位置的成函數 mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  function update(e) {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}
複製代碼
import { useMousePosition } from './mouse'

export default {
  setup() {
    const { x, y } = useMousePosition()
    // 其餘邏輯...
    return { x, y }
  },
}
複製代碼

若是是寫過react hooks的同窗會發現,這與react hooks抽取公用函數的寫法一模一樣。換句話說,寫過二者其一的話,都有助於咱們理解另外一個框架。

雖然vue3的寫法相似於react hooks,可是其內部實現思路是不一樣的:

  1. react hooks函數組件每次渲染的時候會從新執行一遍,而vue3的setup只會在組件初始化的時候執行一次。
  2. react hooks的useState建立組件狀態值,處在一個獨立的閉包空間內,每次使用的時候從這個獨立空間從新獲取值。而vue3的包裝對象則是使用proxy將數據對象變成可追蹤的響應式數據,這個思路跟vue2是同樣的。

vue3的設計在尤大看來至少有一下優點:

  1. 總體上更符合 JavaScript 的直覺
  2. 不受調用順序的限制,能夠有條件地被調用
  3. 不會在後續更新時不斷產生大量的內聯函數而影響引擎優化或是致使 GC 壓力
  4. 不須要老是使用useCallback來緩存子組件的回調防止過分更新
  5. 不須要關注useEffect/useMemo/useCallback的deps依賴數組是否正確的問題

4. 新api所帶來的心智負擔

在閱讀api設計文檔時確實發現一些容易讓開發者搞暈的東西,尤大對此也進行了總結。對於我我的來講有如下比較麻煩的地方:

  1. 使用ref建立的響應式數據難以區分何時須要使用.value方式訪問
  2. 難以區分何時該使用reactive,何時該用ref建立響應式數據
  3. 寫法不正確時,使用reactive建立的響應式數據有丟失響應式的可能
  4. 對setup賦予的職責過大,且建立響應式數據和響應函數的邏輯十分靈活,幾乎沒有限制。會致使不一樣開發者寫出來的代碼可讀性差距極大。

我我的但願vue3正式推出的時候,文檔可能提供api使用推薦寫法,同時能提供相應的lint校驗插件輔助,這樣纔好讓不一樣開發者寫出通用可讀的代碼。

5. vue3的新一代構建工具vite

隨着vue3而來的構建工具不是webpack,而是vite一種新型的項目構建工具。vite做用相似webpack,可是原理與webpack不一樣。對比webpack有如下優點:

  1. 快讀冷啓動
  2. 瞬間熱更新
  3. 真正的按需編譯

我也研究了一下vite的技術原理:

  1. 瀏覽器原生支持(ie除外)的ESM(script module)api,經過在script標籤中添加type="module"就能夠在代碼中使用export import語法,經過瀏覽器端直接導入、導出模塊使用。
  2. ESM的原理是瀏覽器將import導入的模塊轉換成http請求加載對應的資源,須要什麼模塊就加載對應的模塊。
  3. vite將開啓一個本地的koa服務,劫持ESM中由import導入模塊時的http請求,因此vite與webpack不一樣,不會對代碼中所須要的模塊進行靜態分析並抽取成bundle。
  4. vite劫持後import導入的模塊的http請求後,判斷請求文件類型,對不一樣的模塊文件使用不一樣的方法進行解析處理,如對vue文件,使用的就是vue的自帶的模板解析/css解析/等功能,對css文件/ts文件等也是有相應的語法解析工具進行處理。

6. 給計劃使用vue3同窗的一點建議

  1. 能夠對比性的使用react hooks試試看,目前react hooks也相對比較成熟了。
  2. 建議學習typescript,能夠的項目可使用typescript開發試試。

做者簡介: 宮晨光,人和將來大數據前端工程師。

相關文章
相關標籤/搜索