接到新專案,需要用vue3讓我瞅瞅有什麼新特性?

語言: CN / TW / HK

highlight: an-old-hope

1.Vue3簡介

2.vue3--你知道哪些

2.1 效能的提升

  • 打包大小減少41%

  • 初次渲染快55%, 更新渲染快133%

  • 記憶體減少54%

    ......

2.原始碼的升級

  • 使用Proxy代替defineProperty實現響應式

  • 重寫虛擬DOM的實現和Tree-Shaking

    ......

3.擁抱TypeScript

  • Vue3可以更好的支援TypeScript

4.新的特性

  1. Composition API(組合API)

    • setup配置
    • ref與reactive
    • watch與watchEffect
    • provide與inject
    • ......
  2. 新的內建元件

    • Fragment
    • Teleport
    • Suspense
  3. 其他改變

    • 新的生命週期鉤子
    • data 選項應始終被宣告為一個函式
    • 移除keyCode支援作為 v-on 的修飾符
    • ......

5. Composition API 的優勢

  1. Options API 存在的問題

    使用傳統OptionsAPI中,新增或者修改一個需求,就需要分別在data,methods,computed裡修改 。

  2. Composition API 的優勢

    我們可以更加優雅的組織我們的程式碼,函式。讓相關功能的程式碼更加有序的組織在一起。

20220508124639.gif

3.常用 Composition API

3.1 setup

理解:Vue3.0中一個新的配置,值為一個函式

  1. 元件中所用到的:資料、方法等等,均要配置在setup中

  2. setup函式的兩種返回值:

    1. 若返回一個物件,則物件中的屬性、方法, 在模板中均可以直接使用。(重點關注!)
    2. 若返回一個渲染函式:則可以自定義渲染內容。(瞭解)
  3. steup 執行時機

    1. 在beforeCreate之前執行一次,this是undefined。
  4. setup的引數

    • props:值為物件,包含:元件外部傳遞過來,且元件內部宣告接收了的屬性。

    • context:上下文物件

      • attrs: 值為物件,包含:元件外部傳遞過來,但沒有在props配置中宣告的屬性, 相當於 this.$attrs
      • slots: 收到的插槽內容, 相當於 this.$slots
      • emit: 分發自定義事件的函式, 相當於 this.$emit
  5. 注意點:

    1. 儘量不要與Vue2.x配置混用

      • Vue2.x配置(data、methos、computed...)中可以訪問到setup中的屬性、方法。
      • 但在setup中不能訪問到Vue2.x配置(data、methos、computed...)。
      • 如果有重名, setup優先。
    2. setup不能是一個async函式,因為返回值不再是return的物件,而是promise,模板看不到return物件中的屬性。(後期也可以返回一個Promise例項,但需要Suspense和非同步元件的配合)

3.2 ref

  1. 作用:定義一個響應式的資料

  2. 語法: const xxx = ref(initValue)

    • 建立一個包含響應式資料的引用物件(reference物件,簡稱ref物件)
    • JS中操作資料: xxx.value
    • 模板中讀取資料: 不需要.value,直接:<div>{{xxx}}</div>
  3. 其他

    • 接收的資料可以是:基本型別、也可以是物件型別。
    • 基本型別的資料:響應式依然是靠Object.defineProperty()getset完成的。
    • 物件型別的資料:內部 “ 求助 ” 了Vue3.0中的一個新函式—— reactive函式(也就是ES6的Proxy 代理)。

    3.3 reactive函式

  4. 作用: 定義一個物件型別的響應式資料(基本型別不要用它,要用ref函式)

  5. 語法:const 代理物件= reactive(源物件)接收一個物件(或陣列),返回一個代理物件(Proxy的例項物件,簡稱proxy物件)
  6. reactive定義的響應式資料是“深層次的”。
  7. 內部基於 ES6 的 Proxy 實現,通過代理物件操作源物件內部資料進行操作

3.4 computed函式

  • 配置與vue2.x中computed配置功能一致

  • 寫法

    js import {computed} from 'vue' ​ setup(){    ... //計算屬性——簡寫(沒有考慮寫的情況)    let fullName = computed(()=>{        return person.firstName + '-' + person.lastName   })    //計算屬性——完整(考慮讀和寫)    let fullName = computed({        get(){            return person.firstName + '-' + person.lastName       },        set(value){            const nameArr = value.split('-')            person.firstName = nameArr[0]            person.lastName = nameArr[1]       }   }) }

3.5 watch函式

  • 與vue2.x中的watch配置功能一致

  • 支援三個引數

  • 兩個小坑

    • 監視reactive定義的響應式資料時:oldValue無法正確獲取,強制開啟嘗試監視
    • 監視reactive內某個屬性時:deep配置有效,(如物件型別有意義,基本資料型別無意義)

    js import {ref,reactive,watch} from "vue" ​ setup(){       let person=reactive({         name:"張三",         age:18,         job:{           j1:{             salary:20           }         }       })     //情況一:監視ref定義的響應式資料 支援第三個引數      watch(sum,(newValue,oldValue)=>{        console.log('sum變化了',newValue,oldValue)     },{immediate:true}) ​      //情況二:監視多個ref定義的響應式資料      watch([sum,msg],(newValue,oldValue)=>{        console.log('sum或msg變化了',newValue,oldValue)     },) ​      /* 情況三:監視reactive定義的響應式資料           若watch監視的是reactive定義的響應式資料,則無法正確獲得oldValue!!           若watch監視的是reactive定義的響應式資料,則強制開啟了深度監視     */      watch(person,(newValue,oldValue)=>{        console.log('person變化了',newValue,oldValue)     },{immediate:true,deep:false}) //此處的deep配置不再奏效 ​      //情況四:監視reactive定義的響應式資料中的某個屬性      watch(()=>person.job,(newValue,oldValue)=>{        console.log('person的job變化了',newValue,oldValue)     },{immediate:true,deep:true}) ​      //情況五:監視reactive定義的響應式資料中的某些屬性      watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{        console.log('person的job變化了',newValue,oldValue)     },{immediate:true,deep:true}) ​      //特殊情況      watch(()=>person.job,(newValue,oldValue)=>{          console.log('person的job變化了',newValue,oldValue)     },{deep:true}) //此處由於監視的是reactive素定義的物件中的某個屬性,所以deep配置有效 }

3.6 watchEffect函式

  • watch的套路是:既要指明監視的屬性,也要指明監視的回撥

  • watchEffect的套路是:不用指明監視哪個屬性,監視的回撥中用到哪個屬性,那就監視哪個屬性。

  • watchEffect 有點像computed):

    • computed注重的計算出來的值(回撥函式的返回值),所以必須要寫返回值。
    • 而watchEffect更注重的是過程(回撥函式的函式體),所以不用寫返回值。

    js //watchEffect所指定的回撥中用到的資料只要發生變化,則直接重新執行回撥。 watchEffect(()=>{    const x1 = sum.value    const x2 = person.age    console.log('watchEffect配置的回撥執行了') })

3.7 hook函式

  • 什麼是hook?—— 本質是一個函式,把setup函式中使用的Composition API進行了封裝。
  • 類似於vue2.x中的mixin。缺點是元件的data,methods會覆蓋mixins裡的同名data,methods
  • 自定義hook的優勢: 複用程式碼, 讓setup中的邏輯更清楚易懂。
  • Vue3 hook 庫Get Started | VueUse

案例 本地圖片轉 Base64

```js import { onMounted } from 'vue'

type Options = {    el: string }

type Return = {    Baseurl: string | null } export default function (option: Options): Promise {

return new Promise((resolve) => {        onMounted(() => {            const file: HTMLImageElement = document.querySelector(option.el) as HTMLImageElement;            file.onload = ():void => {                resolve({                    Baseurl: toBase64(file)               })           }

})

const toBase64 = (el: HTMLImageElement): string => {            const canvas: HTMLCanvasElement = document.createElement('canvas')            const ctx = canvas.getContext('2d') as CanvasRenderingContext2D            canvas.width = el.width            canvas.height = el.height            ctx.drawImage(el, 0, 0, canvas.width,canvas.height)            console.log(el.width);                        return canvas.toDataURL('image/png')

}   })

} ```

使用

js import useBase64 from './hooks' ​ useBase64({el:'#img'}).then(res=>{  console.log(res) })

3.8 toRef

  • 作用:建立一個 ref 物件,其value值指向另一個物件中的某個屬性。

  • 語法:const name = toRef(obj, 'bar')

  • 應用: 要將響應式物件中的某個屬性單獨提供給外部使用時。

    ```js

    ```

3.9 toRefs

  • 擴充套件:toRefstoRef功能一致,但可以批量建立多個 ref 物件,語法:toRefs(person)

  • 應用:批量建立ref物件主要 是方便我們解構使用

    ```js import { reactive, toRefs } from 'vue' const obj = reactive({   foo: 1,   bar: 1 })

    let { foo, bar } = toRefs(obj)

    foo.value++ console.log(foo, bar); ```

4 . 其他 Composition API

4.1shallowReactive 與 shallowRef

  • shallowReactive:只處理物件最外層屬性的響應式(淺響應式)

  • shallowRef:只處理基本資料型別的響應式, 不進行物件的響應式處理。

  • 什麼時候使用?

    • 如果有一個物件資料,結構比較深,但變化 時只是外層屬性變化==>

      shallowReactive

    • 如果有一個物件資料,後續功能不會修改該物件中的屬性,而是生新的物件來替換===> shallowRef

4.2 readonly 與shallowReadonly

  • readonly: 讓一個響應式資料變為只讀的(深只讀)。
  • shallowReadonly:讓一個響應式資料變為只讀的(淺只讀)。
  • 應用場景: 不希望資料被修改時。

4.3 toRaw 與 markRaw

  • toRaw

    • 作用:將一個由 reactive 生成的 響應式物件 轉為 普通物件
    • 使用場景:用於讀取響應式物件=>對應的普通物件,對這個普通物件的所有操作,不會引起頁面的更新
  • markRaw

    • 作用:標記一個物件,使用其永遠不會再成為響應式物件

    • 應用場景:

      • 有些值不應被設定為響應式的,例如複雜的第三方類庫
      • 當渲染具有不可變資料來源的大列表時,跳過響應式轉換可以提高效能

4.4 customRef

  • 作用:建立一個自定義的ref、並對其依賴項跟蹤和更新觸發進行顯示控制

  • 實現防抖效果:

    js <template> <input type="text" v-model="keyword"> <h3>{{keyword}}</h3> </template> ​ <script> import {ref,customRef} from 'vue' export default { name:'Demo', setup(){ // let keyword = ref('hello') //使用Vue準備好的內建ref //自定義一個myRef function myRef(value,delay){ let timer //通過customRef去實現自定義 return customRef((track,trigger)=>{ return{ get(){ track() //告訴Vue這個value值是需要被“追蹤”的 return value }, set(newValue){ clearTimeout(timer) timer = setTimeout(()=>{ value = newValue trigger() //告訴Vue去更新介面 },delay) } } }) } let keyword = myRef('hello',500) //使用程式設計師自定義的ref return { keyword } } } </script>

4.5 provide 與 inject

  • 作用:實現 祖與後代元件間通訊 (如是 父子 之間通訊 建議使用props)

  • 套路: 父元件有一個 provide 選項來提供資料,後代元件有一個 inject 選項來開始使用這些資料

  • 具體寫法:

    注意:如果傳遞普通的值 是不具有響應式的 需要通過ref reactive 新增響應式

  • 祖元件中:

    js setup(){ ......    let car = reactive({name:'賓士',price:'40萬'})    provide('car',car) //可以用來傳遞響影資料    ...... } 0. 後代元件中:

    js setup(props,context){ ......    const car = inject('car')    return {car} ...... }

4.6響應式資料的判斷

  • isRef: 檢查一個值是否為一個 ref 物件
  • isReactive: 檢查一個物件是否是由 reactive 建立的響應式代理
  • isReadonly: 檢查一個物件是否是由 readonly 建立的只讀代理
  • isProxy: 檢查一個物件是否是由 reactive 或者 readonly 方法建立的代理

5.語法糖

5.1 setup

```js

```

  1. <script setup> 包裹 的任何在內部宣告的頂級繫結(包括變數,函式宣告和匯入)都可以直接在模板中使用

  2. setup() 函式返回的值 ,引用在模板中引用時會自動展開

    js <script setup> // variable const msg = 'Hello!' ​ // functions function log() {  console.log(msg) } </script> ​ <template>  <div @click="log">{{ msg }}</div> </template>

  3. 使用元件

    建議PascalCase 命名的元件標籤以保持一致性,它有助於區分原生自定義元素

    js <script setup> import MyComponent from './MyComponent.vue' </script> ​ <template>  <MyComponent /> //不使用my-Component是因為區別是否為原生元素 </template>

  1. 動態元件

    在setup的語法糖的便攜下,由於元件被引用為變數而不是在字串鍵下注冊,因此

    :is 在內部使用動態元件時應該使用動態繫結<script setup>

    js <script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' </script> ​ <template>  <component :is="Foo" />  <component :is="someCondition ? Foo : Bar" /> </template>

  2. 元件之間的傳值

    要宣告具有完整型別推斷支援的選項propsemits我們可以使用definePropsdefineEmitsAPI,它們在內部自動可用<script setup>

    js <script setup> const props = defineProps({  foo: String }) ​ const emit = defineEmits(['change', 'delete']) // setup code </script>

  1. 頂層 await

    頂層await可以在裡面使用<script setup>。生成的程式碼將編譯為async setup()

    js <script setup> const post = await fetch(`/api/post/1`).then((r) => r.json()) </script>

  1. useSlots()&useAttrs()

    • 插槽 useSlots() ==setupContext.slots
    • useAttrs() 相當於 setupContext.attrs

    您可以直接在模板$slots中訪問它們。$attrs在您確實需要它們的極少數情況下,請分別使用useSlotsuseAttrs助手:

  2. 對外暴露屬性 (defineExpose)

    在vue3.x的setup語法糖中定義的變數預設不會暴露出去,需要使用defineExpose({}) 來暴露元件內部屬性給父元件使用為了在