溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue3.0新特性以及使用的示例分析

發(fā)布時間:2021-09-06 14:49:30 來源:億速云 閱讀:155 作者:小新 欄目:web開發(fā)

這篇文章給大家分享的是有關(guān)Vue3.0新特性以及使用的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

為什么要升級 Vue3

使用 Vue2.x 的小伙伴都熟悉,Vue2.x 中所有數(shù)據(jù)都是定義在data中,方法定義在methods中的,并且使用this來調(diào)用對應(yīng)的數(shù)據(jù)和方法。那 Vue3.x 中就可以不這么玩了, 具體怎么玩我們后續(xù)再說, 先說一下 Vue2.x 版本這么寫有什么缺陷,所有才會進(jìn)行升級變更的。

回顧 Vue2.x 實現(xiàn)加減 

<template>    <div class="homePage">      <p>count: {{ count }}</p>         <p>倍數(shù): {{ multiple }}</p>           <div>        <button style="margin-right: 10px" @click="increase">加1</button>        <button @click="decrease">減一</button>          </div>          </div>  </template>  <script>  export default {    data() {      return { count: 0 };    },    computed: {      multiple() {        return 2 * this.count;      },    },    methods: {      increase() {        this.count++;     },      decrease() {        this.count--;      },    },  };  </script>

上面代碼只是實現(xiàn)了對count的加減以及顯示倍數(shù), 就需要分別在 data、methods、computed 中進(jìn)行操作,當(dāng)我們增加一個需求,就會出現(xiàn)下圖的情況:

Vue3.0新特性以及使用的示例分析

當(dāng)我們業(yè)務(wù)復(fù)雜了就會大量出現(xiàn)上面的情況, 隨著復(fù)雜度上升,就會出現(xiàn)這樣一張圖, 每個顏色的方塊表示一個功能:

Vue3.0新特性以及使用的示例分析

甚至一個功能還有會依賴其他功能,全攪合在一起。

當(dāng)這個組件的代碼超過幾百行時,這時增加或者修改某個需求, 就要在 data、methods、computed 以及 mounted 中反復(fù)的跳轉(zhuǎn),這其中的的痛苦寫過的都知道。

那我們就想啊, 如果可以按照邏輯進(jìn)行分割,將上面這張圖變成下邊這張圖,是不是就清晰很多了呢, 這樣的代碼可讀性和可維護(hù)性都更高:

Vue3.0新特性以及使用的示例分析

那么 vue2.x 版本給出的解決方案就是 Mixin, 但是使用 Mixin 也會遇到讓人苦惱的問題:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  命名沖突問題

  3.  不清楚暴露出來的變量的作用

  4.  邏輯重用到其他 component 經(jīng)常遇到問題

關(guān)于上面經(jīng)常出現(xiàn)的問題我就不一一舉例了,使用過的小伙伴多多少少都會遇到。文章的重點不是 Mixin, 如果確實想知道的就留言啦~

所以,我們 Vue3.x 就推出了Composition API主要就是為了解決上面的問題,將零散分布的邏輯組合在一起來維護(hù),并且還可以將單獨的功能邏輯拆分成單獨的文件。接下來我們就重點認(rèn)識Composition API。

Composition API

Vue3.0新特性以及使用的示例分析

setup

setup 是 Vue3.x 新增的一個選項, 他是組件內(nèi)使用 Composition API的入口。

setup 執(zhí)行時機(jī)

我在學(xué)習(xí)過程中看到很多文章都說 setup 是在 beforeCreate和created之間, 這個結(jié)論是錯誤的。實踐是檢驗真理的唯一標(biāo)準(zhǔn), 于是自己去檢驗了一下:

export default defineComponent({    beforeCreate() {      console.log("----beforeCreate----");    },    created() {      console.log("----created----");    },    setup() {      console.log("----setup----");    },  });

Vue3.0新特性以及使用的示例分析

setup 執(zhí)行時機(jī)是在 beforeCreate 之前執(zhí)行,詳細(xì)的可以看后面生命周期講解。

setup 參數(shù)

使用setup時,它接受兩個參數(shù):

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  props: 組件傳入的屬性

  3.  context

setup 中接受的props是響應(yīng)式的, 當(dāng)傳入新的 props 時,會及時被更新。由于是響應(yīng)式的, 所以不可以使用 ES6 解構(gòu),解構(gòu)會消除它的響應(yīng)式。錯誤代碼示例, 這段代碼會讓 props 不再支持響應(yīng)式:

// demo.vue  export default defineComponent ({      setup(props, context) {          const { name } = props          console.log(name)      },  })

那在開發(fā)中我們想要使用解構(gòu),還能保持props的響應(yīng)式,有沒有辦法解決呢?大家可以思考一下,在后面toRefs學(xué)習(xí)的地方為大家解答。接下來我們來說一下setup接受的第二個參數(shù)context,我們前面說了setup中不能訪問 Vue2 中最常用的this對象,所以context中就提供了this中最常用的三個屬性:attrs、slot 和emit,分別對應(yīng) Vue2.x 中的 $attr屬性、slot插槽 和$emit發(fā)射事件。并且這幾個屬性都是自動同步最新的值,所以我們每次使用拿到的都是最新值。

reactive、ref 與 toRefs

在 vue2.x 中, 定義數(shù)據(jù)都是在data中, 但是 Vue3.x 可以使用reactive和ref來進(jìn)行數(shù)據(jù)定義。那么ref和reactive他們有什么區(qū)別呢?分別什么時候使用呢?說到這里,我又不得不提一下,看到很多網(wǎng)上不少文章說 (reactive用于處理對象的雙向綁定,ref則處理 js 基礎(chǔ)類型的雙向綁定)。我其實不太贊同這樣的說法,這樣很容易初學(xué)者認(rèn)為ref就能處理 js 基本類型, 比如ref也是可以定義對象的雙向綁定的啊, 上段代碼:

setup() {     const obj = ref({count:1, name:"張三"})     setTimeout(() =>{         objobj.value.count = obj.value.count + 1         obj.value.name = "李四"     }, 1000)     return{         obj     }   }

我們將obj.count和obj.name綁定到頁面上也是可以的;但是reactive函數(shù)確實可以代理一個對象, 但是不能代理基本類型,例如字符串、數(shù)字、boolean 等。接下來使用代碼展示一下ref、reactive的使用:

Vue3.0新特性以及使用的示例分析

運行效果:

Vue3.0新特性以及使用的示例分析

上面的代碼中,我們綁定到頁面是通過user.name,user.age;這樣寫感覺很繁瑣,我們能不能直接將user中的屬性解構(gòu)出來使用呢? 答案是不能直接對user進(jìn)行結(jié)構(gòu), 這樣會消除它的響應(yīng)式, 這里就和上面我們說props不能使用 ES6 直接解構(gòu)就呼應(yīng)上了。那我們就想使用解構(gòu)后的數(shù)據(jù)怎么辦,解決辦法就是使用toRefs。toRefs 用于將一個 reactive 對象轉(zhuǎn)化為屬性全部為 ref 對象的普通對象。具體使用方式如下:

<template>    <div class="homePage">      <p>第 {{ year }} 年</p>      <p>姓名:{{ nickname }}</p>      <p>年齡:{{ age }}</p>    </div>  </template>  <script>  import { defineComponent, reactive, ref, toRefs } from "vue";  export default defineComponent({    setup() {      const year = ref(0);      const user = reactive({ nickname: "xiaofan", age: 26, gender: "女" });      setInterval(() => {        year.value++;        user.age++;      }, 1000);      return {        year,        // 使用reRefs        ...toRefs(user),      };    },  });  </script>

生命周期鉤子

我們可以直接看生命周期圖來認(rèn)識都有哪些生命周期鉤子 (圖片是根據(jù)官網(wǎng)翻譯后繪制的):

Vue3.0新特性以及使用的示例分析

從圖中我們可以看到 Vue3.0 新增了setup,這個在前面我們也詳細(xì)說了, 然后是將 Vue2.x 中的beforeDestroy名稱變更成beforeUnmount; destroyed 表更為 unmounted,作者說這么變更純粹是為了更加語義化,因為一個組件是一個mount和unmount的過程。其他 Vue2 中的生命周期仍然保留。上邊生命周期圖中并沒包含全部的生命周期鉤子, 還有其他的幾個, 全部生命周期鉤子如圖所示:

Vue3.0新特性以及使用的示例分析

我們可以看到beforeCreate和created被setup替換了(但是 Vue3 中你仍然可以使用, 因為 Vue3 是向下兼容的, 也就是你實際使用的是 vue2 的)。其次,鉤子命名都增加了on; Vue3.x 還新增用于調(diào)試的鉤子函數(shù)onRenderTriggered和onRenderTricked 下面我們簡單使用幾個鉤子, 方便大家學(xué)習(xí)如何使用,Vue3.x 中的鉤子是需要從 vue 中導(dǎo)入的:

import { defineComponent, onBeforeMount, onMounted, onBeforeUpdate,onUpdated,  onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked,  onRenderTriggered } from "vue"; export default defineComponent({ //  beforeCreate和created是vue2的 beforeCreate() {  console.log("------beforeCreate-----"); }, created() {  console.log("------created-----"); }, setup() { console.log("------setup-----");  // vue3.x生命周期寫在setup中 onBeforeMount(() => {  console.log("------onBeforeMount-----"); }); onMounted(() => {  console.log("------onMounted-----"); }); // 調(diào)試哪些數(shù)據(jù)發(fā)生了變化  onRenderTriggered((event) =>{ console.log("------onRenderTriggered-----",event);  }) }, });

關(guān)于生命周期相關(guān)的內(nèi)容就介紹到這里,下面我們介紹一下 Vue3.x 中watch有什么不同。

watch 與 watchEffect 的用法

watch 函數(shù)用來偵聽特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用。默認(rèn)情況是惰性的,也就是說僅在偵聽的源數(shù)據(jù)變更時才執(zhí)行回調(diào)。

watch(source, callback, [options])

參數(shù)說明:

  •  source: 可以支持 string,Object,Function,Array; 用于指定要偵聽的響應(yīng)式變量

  •  callback: 執(zhí)行的回調(diào)函數(shù)

  •  options:支持 deep、immediate 和 flush 選項。

接下來我會分別介紹這個三個參數(shù)都是如何使用的, 如果你對 watch 的使用不明白的請往下看:

偵聽 reactive 定義的數(shù)據(jù)

import { defineComponent, ref, reactive, toRefs, watch } from "vue";  export default defineComponent({    setup() {      const state = reactive({ nickname: "xiaofan", age: 20 });      setTimeout(() => {        state.age++;     }, 1000);      // 修改age值時會觸發(fā) watch的回調(diào)      watch(        () => state.age,        (curAge, preAge) => {          console.log("新值:", curAge, "老值:", preAge);        }      );      return {       ...toRefs(state),      };    },  });

偵聽 ref 定義的數(shù)據(jù)

const year = ref(0);  setTimeout(() => {    year.value++;  }, 1000);  watch(year, (newVal, oldVal) => {    console.log("新值:", newVal, "老值:", oldVal);  });

偵聽多個數(shù)據(jù)

上面兩個例子中,我們分別使用了兩個 watch, 當(dāng)我們需要偵聽多個數(shù)據(jù)源時, 可以進(jìn)行合并, 同時偵聽多個數(shù)據(jù):

watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => { console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal, "老值:", oldVal); });

偵聽復(fù)雜的嵌套對象

我們實際開發(fā)中,復(fù)雜數(shù)據(jù)隨處可見, 比如:

const state = reactive({    room: {      id: 100,      attrs: {        size: "140平方米",        type: "三室兩廳",      },    },  });  watch(   () => state.room,    (newType, oldType) => {      console.log("新值:", newType, "老值:", oldType);    },    { deep: true }  );

如果不使用第三個參數(shù)deep:true, 是無法監(jiān)聽到數(shù)據(jù)變化的。前面我們提到,默認(rèn)情況下,watch 是惰性的, 那什么情況下不是惰性的, 可以立即執(zhí)行回調(diào)函數(shù)呢?其實使用也很簡單, 給第三個參數(shù)中設(shè)置immediate: true即可。關(guān)于flush配置,還在學(xué)習(xí),后期會補充

stop 停止監(jiān)聽

我們在組件中創(chuàng)建的watch監(jiān)聽,會在組件被銷毀時自動停止。如果在組件銷毀之前我們想要停止掉某個監(jiān)聽, 可以調(diào)用watch()函數(shù)的返回值,操作如下:

const stopWatchRoom = watch(() => state.room, (newType, oldType) => {      console.log("新值:", newType, "老值:", oldType);  }, {deep:true}); setTimeout(()=>{      // 停止監(jiān)聽      stopWatchRoom()  }, 3000)

還有一個監(jiān)聽函數(shù)watchEffect, 在我看來watch已經(jīng)能滿足監(jiān)聽的需求,為什么還要有watchEffect呢?雖然我沒有 get 到它的必要性,但是還是要介紹一下watchEffect,首先看看它的使用和watch究竟有何不同。

import { defineComponent, ref, reactive, toRefs, watchEffect } from "vue";  export default defineComponent({    setup() {      const state = reactive({ nickname: "xiaofan", age: 20 });      let year = ref(0)      setInterval(() =>{          state.age++          year.value++      },1000)      watchEffect(() => {          console.log(state);          console.log(year);        }      );      return {          ...toRefs(state)      }    },  });

執(zhí)行結(jié)果首先打印一次state和year值;然后每隔一秒,打印state和year值。從上面的代碼可以看出, 并沒有像watch一樣需要先傳入依賴,watchEffect會自動收集依賴, 只要指定一個回調(diào)函數(shù)。在組件初始化時, 會先執(zhí)行一次來收集依賴, 然后當(dāng)收集到的依賴中數(shù)據(jù)發(fā)生變化時, 就會再次執(zhí)行回調(diào)函數(shù)。所以總結(jié)對比如下:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  watchEffect 不需要手動傳入依賴

  3.  watchEffect 會先執(zhí)行一次用來自動收集依賴

  4.  watchEffect 無法獲取到變化前的值, 只能獲取變化后的值

上面介紹了 Vue3 Composition API的部分內(nèi)容, 還有很多非常好用的 API, 建議直接查看官網(wǎng) composition-api。其實我們也能進(jìn)行自定義封裝。

自定義 Hooks

開篇的時候我們使用 Vue2.x 寫了一個實現(xiàn)加減的例子, 這里可以將其封裝成一個 hook, 我們約定這些「自定義 Hook」以 use 作為前綴,和普通的函數(shù)加以區(qū)分。useCount.ts 實現(xiàn):

import { ref, Ref, computed } from "vue";  type CountResultProps = {    count: Ref<number>;    multiple: Ref<number>;    increase: (delta?: number) => void;    decrease: (delta?: number) => void;  }; export default function useCount(initValue = 1): CountResultProps {    const count = ref(initValue);    const increase = (delta?: number): void => {      if (typeof delta !== "undefined") {        count.value += delta;      } else {        count.value += 1;      }    };    const multiple = computed(() => count.value * 2);    const decrease = (delta?: number): void => {      if (typeof delta !== "undefined") {        count.value -= delta;      } else {        count.value -= 1;      }    };    return {      count,      multiple,      increase,      decrease,    };  }

接下來看一下在組件中使用useCount這個 hook:

<template>    <p>count: {{ count }}</p>    <p>倍數(shù): {{ multiple }}</p>    <div>      <button @click="increase()">加1</button>      <button @click="decrease()">減一</button>    </div>  </template>  <script lang="ts">  import useCount from "../hooks/useCount";   setup() {      const { count, multiple, increase, decrease } = useCount(10);          return {              count,              multiple,              increase,              decrease,          };      },  </script>

開篇 Vue2.x 實現(xiàn),分散在data,method,computed等, 如果剛接手項目,實在無法快速將data字段和method關(guān)聯(lián)起來,而 Vue3 的方式可以很明確的看出,將 count 相關(guān)的邏輯聚合在一起, 看起來舒服多了, 而且useCount還可以擴(kuò)展更多的功能。項目開發(fā)完之后,后續(xù)還會寫一篇總結(jié)項目中使用到的「自定義 Hooks 的文章」,幫助大家更高效的開發(fā), 關(guān)于Composition API和自定義 Hooks 就介紹到這里, 接下來簡單介紹一下 vue2.x 與 vue3 響應(yīng)式對比。

簡單對比 vue2.x 與 vue3.x 響應(yīng)式

其實在 Vue3.x 還沒有發(fā)布 bate 的時候, 很火的一個話題就是Vue3.x 將使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty。沒有無緣無故的愛,也沒有無緣無故的恨。為何要將Object.defineProperty換掉呢,咋們可以簡單聊一下。我剛上手 Vue2.x 的時候就經(jīng)常遇到一個問題,數(shù)據(jù)更新了啊,為何頁面不更新呢?什么時候用$set更新,什么時候用$forceUpdate強制更新,你是否也一度陷入困境。后來的學(xué)習(xí)過程中開始接觸源碼,才知道一切的根源都是 Object.defineProperty。對這塊想要深入了解的小伙伴可以看這篇文章 為什么 Vue3.0 不再使用 defineProperty 實現(xiàn)數(shù)據(jù)監(jiān)聽?要詳細(xì)解釋又是一篇文章,這里就簡單對比一下Object.defineProperty 與 Proxy

  1.  Object.defineProperty只能劫持對象的屬性, 而 Proxy 是直接代理對象

由于Object.defineProperty只能劫持對象屬性,需要遍歷對象的每一個屬性,如果屬性值也是對象,就需要遞歸進(jìn)行深度遍歷。但是 Proxy 直接代理對象, 不需要遍歷操作

  2.  Object.defineProperty對新增屬性需要手動進(jìn)行Observe

因為Object.defineProperty劫持的是對象的屬性,所以新增屬性時,需要重新遍歷對象, 對其新增屬性再次使用Object.defineProperty進(jìn)行劫持。也就是 Vue2.x 中給數(shù)組和對象新增屬性時,需要使用$set才能保證新增的屬性也是響應(yīng)式的, $set內(nèi)部也是通過調(diào)用Object.defineProperty去處理的。

Teleport

Teleport 是 Vue3.x 新推出的功能, 沒聽過這個詞的小伙伴可能會感到陌生;翻譯過來是傳送的意思,可能還是覺得不知所以,沒事下邊我就給大家形象的描述一下。

Teleport 是什么呢?

Teleport 就像是哆啦 A 夢中的「任意門」,任意門的作用就是可以將人瞬間傳送到另一個地方。有了這個認(rèn)識,我們再來看一下為什么需要用到 Teleport 的特性呢,看一個小例子:在子組件Header中使用到Dialog組件,我們實際開發(fā)中經(jīng)常會在類似的情形下使用到 Dialog ,此時Dialog就被渲染到一層層子組件內(nèi)部,處理嵌套組件的定位、z-index和樣式都變得困難。Dialog從用戶感知的層面,應(yīng)該是一個獨立的組件,從 dom 結(jié)構(gòu)應(yīng)該完全剝離 Vue 頂層組件掛載的 DOM;同時還可以使用到 Vue 組件內(nèi)的狀態(tài)(data或者props)的值。簡單來說就是,即希望繼續(xù)在組件內(nèi)部使用Dialog, 又希望渲染的 DOM 結(jié)構(gòu)不嵌套在組件的 DOM 中。此時就需要 Teleport 上場,我們可以用<Teleport>包裹Dialog, 此時就建立了一個傳送門,可以將Dialog渲染的內(nèi)容傳送到任何指定的地方。接下來就舉個小例子,看看 Teleport 的使用方式

Teleport 的使用

我們希望 Dialog 渲染的 dom 和頂層組件是兄弟節(jié)點關(guān)系, 在index.html文件中定義一個供掛載的元素:

<body>    <div id="app"></div>    <div id="dialog"></div>  </body>

定義一個Dialog組件Dialog.vue, 留意 to 屬性, 與上面的id選擇器一致:

<template>    <teleport to="#dialog">      <div class="dialog">        <div class="dialog_wrapper">          <div class="dialog_header" v-if="title">            <slot name="header">              <span>{{ title }}</span>            </slot>          </div>        </div>        <div class="dialog_content">          <slot></slot>        </div>        <div class="dialog_footer">          <slot name="footer"></slot>        </div>      </div>    </teleport>  </template>

最后在一個子組件Header.vue中使用Dialog組件, 這里主要演示 Teleport 的使用,不相關(guān)的代碼就省略了。header組件

<div class="header">      ...      <navbar />      <Dialog v-if="dialogVisible"></Dialog>  </div>  ...

Dom 渲染效果如下:

Vue3.0新特性以及使用的示例分析

圖片.png 可以看到,我們使用 teleport 組件,通過 to 屬性,指定該組件渲染的位置與 <div id="app"></div> 同級,也就是在 body 下,但是 Dialog 的狀態(tài) dialogVisible 又是完全由內(nèi)部 Vue 組件控制.

Suspense

Suspense是 Vue3.x 中新增的特性, 那它有什么用呢?別急,我們通過 Vue2.x 中的一些場景來認(rèn)識它的作用。Vue2.x 中應(yīng)該經(jīng)常遇到這樣的場景:

<template>  <div>      <div v-if="!loading">          ...      </div>      <div v-if="loading">          加載中...      </div>  </div>  </template>

在前后端交互獲取數(shù)據(jù)時, 是一個異步過程,一般我們都會提供一個加載中的動畫,當(dāng)數(shù)據(jù)返回時配合v-if來控制數(shù)據(jù)顯示。如果你使用過vue-async-manager這個插件來完成上面的需求, 你對Suspense可能不會陌生,Vue3.x 感覺就是參考了vue-async-manager. Vue3.x 新出的內(nèi)置組件Suspense, 它提供兩個template slot, 剛開始會渲染一個 fallback 狀態(tài)下的內(nèi)容, 直到到達(dá)某個條件后才會渲染 default 狀態(tài)的正式內(nèi)容, 通過使用Suspense組件進(jìn)行展示異步渲染就更加的簡單。:::warning 如果使用 Suspense, 要返回一個 promise :::Suspense 組件的使用:

<Suspense>        <template #default>            <async-component></async-component>        </template>        <template #fallback>            <div>                Loading...            </div>        </template>  </Suspense>

asyncComponent.vue:

<<template>  <div>      <h5>這個是一個異步加載數(shù)據(jù)</h5>      <p>用戶名:{{user.nickname}}</p>      <p>年齡:{{user.age}}</p>  </div>  </template>  <script>  import { defineComponent } from "vue"  import axios from "axios"  export default defineComponent({      setup(){          const rawData = await axios.get("http://xxx.xinp.cn/user")          return {              user: rawData.data          }      }  })  </script>

從上面代碼來看,Suspense 只是一個帶插槽的組件,只是它的插槽指定了default 和 fallback 兩種狀態(tài)。

片段(Fragment)

在 Vue2.x 中, template中只允許有一個根節(jié)點:

<template>      <div>          <span></span>          <span></span>      </div>  </template>

但是在 Vue3.x 中,你可以直接寫多個根節(jié)點, 是不是很爽:

<template>      <span></span>      <span></span>  </template>

更好的 Tree-Shaking

Vue3.x 在考慮到 tree-shaking的基礎(chǔ)上重構(gòu)了全局和內(nèi)部 API, 表現(xiàn)結(jié)果就是現(xiàn)在的全局 API 需要通過 ES Module的引用方式進(jìn)行具名引用, 比如在 Vue2.x 中,我們要使用 nextTick:

// vue2.x  import Vue from "vue"  Vue.nextTick(()=>{      ...  })

Vue.nextTick() 是一個從 Vue 對象直接暴露出來的全局 API,其實 $nextTick() 只是 Vue.nextTick() 的一個簡易包裝,只是為了方便而把后者的回調(diào)函數(shù)的 this 綁定到了當(dāng)前的實例。雖然我們借助webpack的tree-shaking, 但是不管我們實際上是否使用Vue.nextTick(), 最終都會進(jìn)入我們的生產(chǎn)代碼, 因為 Vue 實例是作為單個對象導(dǎo)出的, 打包器無法堅持出代碼總使用了對象的哪些屬性。在 Vue3.x 中改寫成這樣:

import { nextTick } from "vue"  nextTick(() =>{      ... })

受影響的 API

這是一個比較大的變化, 因為以前的全局 API 現(xiàn)在只能通過具名導(dǎo)入,這一更改會對以下 API 有影響:

  •  Vue.nextTick

  •  Vue.observable(用 Vue.reactive 替換)

  •  Vue.version

  •  Vue.compile(僅限完整版本時可用)

  •  Vue.set(僅在 2.x 兼容版本中可用)

  •  Vue.delete(與上同)

內(nèi)置工具

出來上面的 API 外, 還有許多內(nèi)置的組件 以上僅適用于 ES Modules builds,用于支持 tree-shaking 的綁定器&mdash;&mdash;UMD 構(gòu)建仍然包括所有特性,并暴露 Vue 全局變量上的所有內(nèi)容 (編譯器將生成適當(dāng)?shù)妮敵觯允褂萌滞獾?api 而不是導(dǎo)入)。::: 前面都是 Vue3.0 的一些新特性,后面著重介紹一下相對于 Vue2.x 來說, 有什么變更呢?

變更

slot 具名插槽語法

在 Vue2.x 中, 具名插槽的寫法:

<!--  子組件中:-->  <slot name="title"></slot>

在父組件中使用:

<template slot="title">      <h2>歌曲:成都</h2>  <template>

如果我們要在 slot 上面綁定數(shù)據(jù),可以使用作用域插槽,實現(xiàn)如下:

// 子組件  <slot name="content" :data="data"></slot>  export default {      data(){          return{              data:["走過來人來人往","不喜歡也得欣賞","陪伴是最長情的告白"]          }      }  }
<!-- 父組件中使用 -->  <template slot="content" slot-scope="scoped">      <div v-for="item in scoped.data">{{item}}</div>  <template>

在 Vue2.x 中具名插槽和作用域插槽分別使用slot和slot-scope來實現(xiàn), 在 Vue3.0 中將slot和slot-scope進(jìn)行了合并同意使用。Vue3.0 中v-slot:

<!-- 父組件中使用 -->   <template v-slot:content="scoped">     <div v-for="item in scoped.data">{{item}}</div>  </template>  <!-- 也可以簡寫成: -->  <template #content="{data}">      <div v-for="item in data">{{item}}</div>  </template>

自定義指令

首先回顧一下 Vue 2 中實現(xiàn)一個自定義指令:

// 注冊一個全局自定義指令 `v-focus`  Vue.directive('focus', {    // 當(dāng)被綁定的元素插入到 DOM 中時&hellip;&hellip;    inserted: function (el) {      // 聚焦元素      el.focus()    } })

在 Vue 2 中, 自定義指令通過以下幾個可選鉤子創(chuàng)建:

  •  bind:只調(diào)用一次,指令第一次綁定到元素時調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。

  •  inserted:被綁定元素插入父節(jié)點時調(diào)用 (僅保證父節(jié)點存在,但不一定已被插入文檔中)。

  •  update:所在組件的 VNode 更新時調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新 (詳細(xì)的鉤子函數(shù)參數(shù)見下)。

  •  componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。

  •  unbind:只調(diào)用一次,指令與元素解綁時調(diào)用。

在 Vue 3 中對自定義指令的 API 進(jìn)行了更加語義化的修改, 就如組件生命周期變更一樣, 都是為了更好的語義化, 變更如下:

Vue3.0新特性以及使用的示例分析

所以在 Vue3 中, 可以這樣來自定義指令:

const { createApp } from "vue"  const app = createApp({})  app.directive('focus', {      mounted(el) {          el.focus()      }  })

然后可以在模板中任何元素上使用新的 v-focus指令, 如下:

<input v-focus />

v-model 升級

在使用 Vue 3 之前就了解到 v-model 發(fā)生了很大的變化, 使用過了之后才真正的 get 到這些變化, 我們先縱觀一下發(fā)生了哪些變化, 然后再針對的說一下如何使用:

  •  變更:在自定義組件上使用v-model時, 屬性以及事件的默認(rèn)名稱變了

  •  變更:v-bind的.sync修飾符在 Vue 3 中又被去掉了, 合并到了v-model里

  •  新增:同一組件可以同時設(shè)置多個 v-model

  •  新增:開發(fā)者可以自定義 v-model修飾符

有點懵?別著急,往下看 在 Vue2 中, 在組件上使用 v-model其實就相當(dāng)于傳遞了value屬性, 并觸發(fā)了input事件:

<!-- Vue 2 -->  <search-input v-model="searchValue"><search-input>  <!-- 相當(dāng)于 -->  <search-input :value="searchValue" @input="searchValue=$event"><search-input>

這時v-model只能綁定在組件的value屬性上,那我們就不開心了, 我們就像給自己的組件用一個別的屬性,并且我們不想通過觸發(fā)input來更新值,在.async出來之前,Vue 2 中這樣實現(xiàn):

// 子組件:searchInput.vue  export default {      model:{          prop: 'search',          event:'change'      }  }

修改后, searchInput 組件使用v-model就相當(dāng)于這樣:

<search-input v-model="searchValue"><search-input>  <!-- 相當(dāng)于 -->  <search-input :search="searchValue" @change="searchValue=$event"><search-input>

但是在實際開發(fā)中,有些場景我們可能需要對一個 prop 進(jìn)行 “雙向綁定”, 這里以最常見的 modal 為例子:modal 挺合適屬性雙向綁定的,外部可以控制組件的visible顯示或者隱藏,組件內(nèi)部關(guān)閉可以控制 visible屬性隱藏,同時 visible 屬性同步傳輸?shù)酵獠?。組件內(nèi)部, 當(dāng)我們關(guān)閉modal時, 在子組件中以 update:PropName 模式觸發(fā)事件:

this.$emit('update:visible', false)

然后在父組件中可以監(jiān)聽這個事件進(jìn)行數(shù)據(jù)更新:

<modal :visible="isVisible" @update:visible="isVisible = $event"></modal>

此時我們也可以使用v-bind.async來簡化實現(xiàn):

<modal :visible.async="isVisible"></modal>

上面回顧了 Vue2 中v-model實現(xiàn)以及組件屬性的雙向綁定,那么在 Vue 3 中應(yīng)該怎樣實現(xiàn)的呢? 在 Vue3 中, 在自定義組件上使用v-model, 相當(dāng)于傳遞一個modelValue 屬性, 同時觸發(fā)一個update:modelValue事件:

<modal v-model="isVisible"></modal>  <!-- 相當(dāng)于 -->  <modal :modelValue="isVisible" @update:modelValue="isVisible = $event"></modal>

如果要綁定屬性名, 只需要給v-model傳遞一個參數(shù)就行, 同時可以綁定多個v-model:

<modal v-model:visible="isVisible" v-model:content="content"></modal>  <!-- 相當(dāng)于 -->  <modal      :visible="isVisible"      :content="content"      @update:visible="isVisible"      @update:content="content"  />

不知道你有沒有發(fā)現(xiàn),這個寫法完全沒有.async什么事兒了, 所以啊,Vue 3 中又拋棄了.async寫法, 統(tǒng)一使用v-model

異步組件

Vue3 中 使用 defineAsyncComponent 定義異步組件,配置選項 component 替換為 loader ,Loader 函數(shù)本身不再接收 resolve 和 reject 參數(shù),且必須返回一個 Promise,用法如下:

<template>    <!-- 異步組件的使用 -->    <AsyncPage />  </tempate>  <script>  import { defineAsyncComponent } from "vue";  export default {    components: {      // 無配置項異步組件      AsyncPage: defineAsyncComponent(() => import("./NextPage.vue")),      // 有配置項異步組件      AsyncPageWithOptions: defineAsyncComponent({     loader: () => import(".NextPage.vue"),     delay: 200,     timeout: 3000,     errorComponent: () => import("./ErrorComponent.vue"),     loadingComponent: () => import("./LoadingComponent.vue"),   })    },  }  </script>

感謝各位的閱讀!關(guān)于“Vue3.0新特性以及使用的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI