整理分享經典技巧之vue3及與vue2的區別(部分)

2022-01-31 07:00:20
本篇文章給大家帶來了vue3與vue2的部分割區別的相關知識,其中包括生命週期變化、範例變化以及方法變化等等,希望對大家有幫助。

1.生命週期的變化:3.x(上) 2.x(下)

vue3.0
vue2.0

不難看出,vue3.0與vue2.0之間生命週期函數在銷燬的時候有變化:

beforeDestroy --> beforeUnmount
destroyed --> unmounted
其他的區別主要在於書寫使用的語言上的差別
在ts中使用 class 類元件書寫可以 參考 vue-class-component 或者 vue-property-decorator
書寫的風格和vue2.0的選項式區別不大。
如果使用js書寫程式碼 則應當使用組合式。

具體變化帶來的問題,會在下面的組合式寫法中講解。

2.定義全域性變數的方法變化

// 之前(Vue 2.x)
Vue.prototype.$http = () => {}
Vue.prototype.url= 'http://123'
// 之後(Vue 3.x)
const app = createApp({})
app.config.globalProperties.$http = () => {}
app.config.globalProperties.url= 'http://123'

3.建立vue範例變化

//=======vue3.x
//使用createApp函數來範例化vue,
//該函數接收一個根元件選項物件作為第一個引數
//使用第二個引數,我們可以將根 prop 傳遞給應用程式
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App,{ userName: "blackLieo" })
.use(store)
.use(router)
.mount('#app')  
//由於 createApp 方法返回應用範例本身,因此可以在其後鏈式呼叫其它方法,這些方法可以在以下部分中找到。

//=======vue2.x
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
 Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

4.插槽使用變化

//================vue2.0使用插槽基本上直接使用slot進行操作//其中vue2.0經歷了兩次更迭,2.6.0版本slot升級為v-slot<p>
    <slot :current="toolTipData" name="test"></slot> // 具名 作用域插槽
    <slot></slot> //預設插槽</p>//父元件呼叫該元件<test>
    <template>
       <p>預設插槽</p>
    </template>
    // 作用域插槽
    <template slot-scope="{ current }" slot="test">
       <el-form label-width="80px" :model="current">
         <el-form-item label="id:">
           <el-link type="info">{{ current.id }}</el-link>
         </el-form-item>
         <el-form-item label="name:">
           <el-link type="info">{{ current.name }}</el-link>
         </el-form-item>
         <el-form-item label="label:">
           <el-link type="info">{{ current.label }}</el-link>
         </el-form-item>
         <el-form-item label="group:">
           <el-link type="info">{{ current.group }}</el-link>
         </el-form-item>
         <el-form-item label="runtime:">
           <el-link type="info">{{ current.runtime }}</el-link>
         </el-form-item>
         <el-form-item label="category:">
           <el-link type="info">{{ current.category }}</el-link>
         </el-form-item>
       </el-form>
     </template>
 </test>

 
 //==============vue3.0使用插槽//在vue3.0中,插槽使用v-slot 簡寫用#<p>	
   <slot name="test" :newData="slotsData"></slot>
   <slot></slot></p><HelloWorld msg="Welcome to Your Vue.js + TypeScript App">
    <template #default> // 可以寫為v-slot:default  #後面跟的是插槽名稱
       <p>預設插槽</p>
    </template>
    //作用域插槽
    <template #test="{ newData }"> // 可以寫為v-slot:test="newData"
      <p>{{ newData.aa }}</p>
      <p>{{ newData.bb }}</p>
    </template></HelloWorld>//一個元件裡面具有多個插槽時,一定要帶上名稱,否則可能會導致作用域錯亂

5.自定義指令

在 Vue 2 中實現一個自定義指令:

// 註冊一個全域性自定義指令 `v-focus`Vue.directive('focus', {
  // 當被繫結的元素插入到 DOM 中時……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }})

在 Vue 2 中, 自定義指令通過以下幾個可選勾點建立:

  • bind:只呼叫一次,指令第一次繫結到元素時呼叫。在這裡可以進行一次性的初始化設定。
  • inserted:被繫結元素插入父節點時呼叫(僅保證父節點存在,但不一定已被插入檔案中)。
  • update:所在元件的 VNode 更新時呼叫,但是可能發生在其子 VNode更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前後的值來忽略不必要的模板更新 (詳細的勾點函數引數見下)。
  • componentUpdated:指令所在元件的 VNode 及其子 VNode 全部更新後呼叫。
  • unbind:只呼叫一次,指令與元素解綁時呼叫。

在 Vue 3 中對自定義指令的 API 進行了更加語意化的修改, 就如元件生命週期變更一樣, 都是為了更好的語意化, 變更如下:
指令函數變化

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

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

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

<input v-focus />

6.v-model 升級

Vue 3 中 v-model 發生了很大的變化:

變更:在自定義元件上使用v-model時, 屬性以及事件的預設名稱變了
變更:v-bind的.sync修飾符在 Vue 3 中又被去掉了, 合併到了v-model裡
新增:同一元件可以同時設定多個 v-model
新增:開發者可以自定義 v-model修飾符

我們來詳細瞭解一下,並對比一下vue2與vue3 在元件上使用 v-model,
例如:
vue2 的輸入框的雙向繫結,其實就相當於傳遞了value屬性, 並觸發了input事件:

<!-- Vue 2 -->
<input v-model="searchValue"><input>

<!-- 相當於 -->
<input :value="searchValue" @input="searchValue=$event"><input>

這時v-model只能繫結在元件的value屬性上,那如果我們要給自己的元件用一個別的屬性,並且我們不想通過觸發input來更新值。

但是在實際開發中,有些場景我們可能需要對一個 prop 進行 「雙向繫結」, 這裡以最常見的 dialog 為例子:dialog 挺合適屬性雙向繫結的,
外部可以控制元件的visible顯示或者隱藏,元件內部關閉可以控制 visible屬性隱藏,同時 visible 屬性同步傳輸到外部。元件內部, 
當我們關閉dialog時, 在子元件中以 update:PropName 模式觸發事件。

事件為

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

然後在父元件中可以監聽這個事件進行資料更新:

<el-dialog :visible="isVisible" @update:visible="isVisible = $event"></el-dialog>

在vue2的開發中,我們實際會發現一個新的東西sync,所以也可以使用v-bind.sync來簡化實現:

<el-dialog :visible.sync="isVisible"></el-dialog>

上面說了 Vue2 中v-model實現以及元件屬性的雙向繫結,那麼在 Vue 3 中應該怎樣實現的呢?
在 Vue3 中, 在自定義元件上使用v-model, 相當於傳遞一個modelValue 屬性, 同時觸發一個update:modelValue事件:

<el-dialog v-model="isVisible"></el-dialog>
<!-- 相當於 -->
<el-dialog :modelValue="isVisible" @update:modelValue="isVisible = $event"></el-dialog>

如果要繫結屬性名, 只需要給v-model傳遞一個引數就行, 同時可以繫結多個v-model

<el-dialog v-model:visible="isVisible" v-model:content="content"></el-dialog><!-- 相當於 --><el-dialog    :visible="isVisible"
    :content="content"
    @update:visible="isVisible"
    @update:content="content"></el-dialog>

這個寫法完全沒有.sync什麼事兒了, Vue 3 中拋棄了.sync寫法, 統一使用v-model。

7.非同步元件的使用

Vue3 中 使用 defineAsyncComponent 定義非同步元件,設定選項 component 替換為 loader ,Loader 函數本身不再接收 resolve 和 reject 引數,且必須返回一個 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>

8.Composition API

   使用Composition API 解決我們在完成功能時,在 data、methods、computed 以及 mounted 中反覆的跳轉,他將零散分佈的
   邏輯組合在一起維護,並可以將單獨的邏輯再分為單獨的檔案

如果想要了解<script setup></script>語法糖,請移步 vue3 setup語法糖(部分總結)

我們先來了解一下Composition具有的API
composition

  • setup

setup 是 Vue3.x 新增的一個選項, 他是元件內使用 Composition API的入口。

setup 執行時機
通過一段程式碼 我們可以知道:

<script>import { defineComponent, reactive } from "vue";export default defineComponent({
  beforeCreate() {
    console.log("----beforeCreate----");
  },
  created() {
    console.log("----created----");
  },
  setup() {
    const state = reactive({ count: 0 });
    console.log("----setup----");
    return {
      state,
    };
  },});</script>

會出現如下所示的輸出結果:
在這裡插入圖片描述

setup 執行時機是在 beforeCreate 之前執行,詳細的可以看後面生命週期講解。

  1. setup 引數

使用setup時,它接受兩個引數:

  • props: 元件傳入的屬性
  • context

setup 中接受的props是響應式的, 當傳入新的 props 時,會及時被更新。
由於是響應式的, 所以不可以使用 ES6 解構,解構會消除它的響應式。
錯誤程式碼範例, 這段程式碼會讓 props 不再支援響應式:

setup(props) {
    const { name } = props;
    console.log(name);
    const state = reactive({ count: 0 });
    return {
      state,
    };
  },

對於以上的的問題,我們可以在後面的toRefs進行解釋。
現在我們就來講一下:

  1. reactive、ref、toRefs、readonly

在 vue2.x 中, 定義資料都是在data中。
但是 Vue3.x 可以使用reactiveref來進行資料定義。
那麼ref和reactive他們有什麼區別呢?

reactive用於處理物件的雙向繫結,ref處理 js 基礎型別或者處理物件的雙向繫結。
注意refs
它接受一個內部值並返回一個響應式且可變的 ref 物件。ref 物件具有指向內部值的單個 property.value。
<template>
  <p>
    <p>計數:{{ num }}s</p>
    <p>主人年齡:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>動物類別:{{ animal.type }}</p>
    <p>動物名稱:{{ animal.name }}</p>
    <p>動物年齡:{{ animal.age }}</p>
  </p></template><script>import { defineComponent, reactive, ref } from "vue";export default defineComponent({
  setup() {
    //使用ref宣告基本型別
    const num = ref(0);
    //使用ref宣告物件
    const person = ref({ age: 20, name: "張三" });
    //使用reactive宣告物件
    const animal = reactive({ type: "貓", name: "小花", age: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.age++;
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    return {
      num,
      animal,
      person,
    };
  },});</script>

我們繫結到頁面是通過user.name,user.age;這樣寫感覺很繁瑣,我們能不能直接將user中的屬性解構出來使用呢?
答案是不能直接對user進行結構, 這樣會消除它的響應式, 這裡就和上面我們說props不能使用 ES6 直接解構就呼應上了。
那我們就想使用解構後的資料怎麼辦,解決辦法就是使用toRefs
toRefs 用於將一個 reactive 物件轉化為屬性全部為 ref 物件的普通物件。具體使用方式如下:

<template>
  <p>
    <p>計數:{{ num }}s</p>
    <p>主人年齡:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>動物類別:{{ atype }}</p>
    <p>動物名稱:{{ aname }}</p>
    <p>動物年齡:{{ aage }}</p>
  </p></template><script>import { defineComponent, reactive, ref, toRefs } from "vue";export default defineComponent({
  setup() {
    //使用ref宣告基本型別
    const num = ref(0);
    //使用ref宣告物件
    const person = ref({ age: 20, name: "張三" });
    //使用reactive宣告物件
    const animal = reactive({ atype: "貓", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    return {
      num,
      person,
      ...toRefs(animal),
    };
  },});</script>

有時我們想跟蹤響應式物件 (ref 或 reactive) 的變化,但我們也希望防止在應用程式的某個位置更改它,這時候,我們就需要readonly
例如,當我們有一個被傳遞的響應式物件時,我們不想讓它在傳遞的的時候被改變。為此,我們可以基於原始物件建立一個唯讀的 proxy 物件:

import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })

const copy = readonly(original)

// 通過 original 修改 count,將會觸發依賴 copy 的偵聽器

original.count++

// 通過 copy 修改 count,將導致失敗並出現警告
copy.count++ // 警告: "Set operation on key 'count' failed: target is readonly."
  • 生命週期勾點

在最開始的時候,我們就看到了兩張圖,其中的變化我們可以總結一下

生命週期

我們可以看到beforeCreatecreatedsetup替換了(但是 Vue3 中你仍然可以使用, 因為 Vue3 是向下相容的, 也就是你實際使用的是 vue2 的)。
其次,勾點命名都增加了on; Vue3.x 還新增用於偵錯的勾點函數onRenderTriggeredonRenderTricked
下面我們簡單使用幾個勾點, 方便大家學習如何使用,Vue3.x 中的勾點是需要從 vue 中匯入的:

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-----");
    });
    onBeforeUpdate(() => {
      console.log("------onBeforeUpdate-----");
    });
    onUpdated(() => {
      console.log("------onUpdated-----");
    });
    onBeforeUnmount(() => {
      console.log("------onBeforeUnmount-----");
    });
    onUnmounted(() => {
      console.log("------onUnmounted-----");
    });
    onErrorCaptured(() => {
      console.log("------onErrorCaptured-----");
    });
    onRenderTracked(() => {
      console.log("------onRenderTracked-----");
    });
    // 偵錯哪些資料發生了變化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event);
    });
  },});

具體怎麼使用,是幹什麼的請參考官方檔案,這裡就不一一贅述了。

  • watch 與 watchEffect 的用法

watch 函數用來偵聽特定的資料來源,並在回撥函數中執行副作用。預設情況是惰性的,也就是說僅在偵聽的源資料變更時才執行回撥。

watch(source, callback, [options])

引數說明:

  1. source: 可以支援 string,Object,Function,Array;
  2. 用於指定要偵聽的響應式變數 callback:
  3. 執行的回撥函數 options:支援 deep、immediate 和 flush 選項。

其實整體和原來的vue2.0有一定的相似性,基本引數沒有發生大的改變。

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

//監聽reactive物件:watch(
      () => animal.aage,
      (curAge, preAge) => {
        console.log("新值:", curAge, "老值:", preAge);
      }
    );//監聽ref變數
 watch(num, (newVal, oldVal) => {
      console.log("新值:", newVal, "老值:", oldVal);
    });
    //多個值的監聽
 watch([() => animal.aage, num], ([curAge, newVal], [preAge, oldVal]) => {
      console.log("新值:", curAge, "老值:", preAge);
      console.log("新值:", newVal, "老值:", oldVal);
    });
    //監聽物件複雜時,請使用深度監聽 讓函數的第三個引數為deep:truewatch(
      () => state.animal,
      (newType, oldType) => {
        console.log("新值:", newType, "老值:", oldType);
      },
      { deep: true }
    );

預設情況下,watch 是惰性的, 那什麼情況下不是惰性的, 可以立即執行回撥函數呢?其實使用也很簡單, 給第三個引數中設定
immediate: true即可。

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

還有一個監聽函數watchEffect,介紹一下watchEffect,看看它的使用和watch究竟有何不同,在上面程式碼的基礎上,我們來編寫。

watchEffect(() => {
      console.log(num);
    });

執行結果首先列印一次num值;然後每隔一秒,列印num值。
從上面的程式碼可以看出, 並沒有像watch一樣需要先傳入依賴,watchEffect會自動收集依賴, 只要指定一個回撥函數。在元件初始化時, 會先執行一次來收集依賴, 然後當收集到的依賴中資料發生變化時, 就會再次執行回撥函數。所以總結對比如下:

  • watchEffect 不需要手動傳入依賴
  • watchEffect 會先執行一次用來自動收集依賴
  • watchEffect無法獲取到變化前的值, 只能獲取變化後的值

9.Hooks

在之前vue用的mixin,所謂的混入 (mixin) 提供了一種非常靈活的方式,來分發 Vue 元件中的可複用功能。一個混入物件可以包含任意元件選項。當元件使用混入物件時,所有混入物件的選項將被「混合」進入該元件本身的選項。
現在的vue3.0提供了一種新的東西 vue-hooks。具體的vue-hooks,請參考網上教學

為什麼產生了hooks
首先從class-component/vue-options說起:

  • 跨元件程式碼難以複用
  • 大元件,維護困難,顆粒度不好控制,細粒度劃分時,元件巢狀存層次太深影響效能
  • 類元件,this不可控,邏輯分散,不容易理解
  • mixins具有副作用,邏輯互相巢狀,資料來源不明,且不能互相消費

當一個模版依賴了很多mixin的時候,很容易出現資料來源不清或者命名衝突的問題,而且開發mixins的時候,邏輯及邏輯依賴的屬性互相分散且mixin之間不可互相消費。這些都是開發中令人非常痛苦的點。
hooks存在以下優勢:

  • 允許hooks間相互傳遞值
  • 元件之間重用狀態邏輯
  • 明確指出邏輯來自哪裡

我們先來封裝一個hooks,假如這個hooks是一個實現年齡加減,獲取雙倍年齡的函數。用單獨的檔案儲存 bus/useAge.ts

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

在元件裡面呼叫該hooks

<template>
  <p>
    <p>計數:{{ num }}s</p>
    <p>主人年齡:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>動物類別:{{ atype }}</p>
    <p>動物名稱:{{ aname }}</p>
    <p>動物年齡:{{ aage }}</p>
    <p>count: {{ age }}</p>
    <p>雙倍年齡: {{ doubleAge }}</p>
    <p>
      <button @click="increase()">加1</button>
      <button @click="decrease()">減一</button>
    </p>
  </p></template><script>import {
  defineComponent,
  reactive,
  ref,
  toRefs,
  watch,
  watchEffect,} from "vue";import useAge from "../bus/useAge.ts";export default defineComponent({
  setup() {
    //使用ref宣告基本型別
    const num = ref(0);
    //使用ref宣告物件
    const person = ref({ age: 20, name: "張三" });
    //使用reactive宣告物件
    const animal = reactive({ atype: "貓", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
      animal.aname = "小橘";
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    const { age, doubleAge, increase, decrease } = useAge(22);
    return {
      num,
      person,
      ...toRefs(animal),
      age,
      doubleAge,
      increase,
      decrease,
    };
  },});</script>

10.vue2.x 與vue3.x的響應式資料對比

在vue2.0中,應該很多人都使用了$set這個東西吧。
資料更新了,頁面為什麼不變化呢?什麼時候我們用$forceUpdate強制更新呢?
在vue2.x中,實現資料監聽使用的是Object.defineProperty。而vue3.x使用的是Proxy

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

由於Object.defineProperty只能劫持物件屬性,需要遍歷物件的每一個屬性,如果屬性值也是物件,就需要遞迴進行深度遍歷。
但是Proxy 直接代理物件, 不需要遍歷操作

  1. Object.defineProperty對新增屬性需要手動進行Observe

因為Object.defineProperty劫持的是物件的屬性,所以新增屬性時,需要重新遍歷物件,
對其新增屬性再次使用Object.defineProperty進行劫持。也就是 Vue2.x
中給陣列和物件新增屬性時,需要使用$set才能保證新增的屬性也是響應式的,
$set內部也是通過呼叫Object.defineProperty去處理的。

  1. Proxy有多種攔截方法,如apply,deleteProperty等等,是Object.defineProperty()不具備的。
  2. Proxy是返回值是一個物件,可以直接進行操作,而defineProperty()要先遍歷所有物件屬性值才能進行操作。
    但是相對來說,Object.defineProperty()相容性高一些。

11.Teleport

Teleport 是 Vue3.x 新推出的功能

Teleport 是什麼呢?

Teleport 就像是哆啦 A 夢中的「任意門」,任意門的作用就是可以將人瞬間傳送到另一個地方。有了這個認識,我們再來看一下為什麼需要用到 Teleport 的特性呢,看一個小例子:

例如我們在使用Dialog元件時,我們實際開發中經常會使用到 Dialog,此時Dialog就被渲染到一層層子元件內部,處理巢狀元件的定位、z-index和樣式都變得困難。但是元件希望位於頁面的最上方,這時候我們將Dialog元件掛載在body上面是最好控制的,我們能夠很好的通過zIndex來控制Dialog的位置,當他巢狀在templat裡面的時候就不那麼容易了。簡單來說就是,即希望繼續在元件內部使用Dialog,又希望渲染的 DOM 結構不巢狀在元件內部的 DOM 中。

此時就需要 Teleport 上場,我們可以用<Teleport>包裹Dialog, 此時就建立了一個傳送門,可以將Dialog渲染的內容傳送到任何指定的地方。

<template>
  <p>
    <p>計數:{{ num }}s</p>
    <p>主人年齡:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>動物類別:{{ atype }}</p>
    <p>動物名稱:{{ aname }}</p>
    <p>動物年齡:{{ aage }}</p>
    <p>count: {{ age }}</p>
    <p>倍數: {{ doubleAge }}</p>
    <p>
      <button @click="increase()">加1</button>
      <button @click="decrease()">減一</button>
    </p>
    <el-button type="text" @click="dialogVisible = true"
      >點選開啟 Dialog</el-button    >
    <teleport to="#dialogLL">
      <el-dialog title="提示" v-model="dialogVisible" width="30%">
        <span>這是一段資訊</span>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="dialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="dialogVisible = false"
              >確 定</el-button            >
          </span>
        </template>
      </el-dialog>
    </teleport>
  </p></template><script>import {
  defineComponent,
  reactive,
  ref,
  toRefs,} from "vue";export default defineComponent({
  setup() {
    //使用ref宣告基本型別
    const num = ref(0);
    const dialogVisible = ref(false);
    //使用ref宣告物件
    const person = ref({ age: 20, name: "張三" });
    //使用reactive宣告物件
    const animal = reactive({ atype: "貓", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
      animal.aname = "小橘";
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);

    return {
      dialogVisible,
      num,
      person,
      ...toRefs(animal),
    };
  },});</script>

我們可以很清楚的看到teleport上有一個to屬性,這個屬性是講當前節點傳送到制定位置去的。位置應該傳送到哪裡呢?
答案就是在index.html上面
下面是我們的首頁 你會看到有一個p的ID名為dialogLL,teleport就將節點掛載在這裡來了

<!DOCTYPE html><html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <p id="app"></p>
    <p id="dialogLL"></p>
    <script type="module" src="/src/main.js"></script>
  </body></html>

如圖所示:
在這裡插入圖片描述

12.Suspense

Suspense是 Vue3.x 中新增的特性, 那它有什麼用呢?我們通過 Vue2.x 中的一些場景來認識它的作用。 Vue2.x 中應該經常遇到這樣的場景:

<template>
  <p>
    <p v-if="!loading">...</p>
    <p v-if="loading">載入中...</p>
  </p></template>

在前後端互動獲取資料時, 是一個非同步過程,一般我們都會提供一個載入中的動畫,當資料返回時配合v-if來控制資料顯示。
它提供兩個template slot, 剛開始會渲染一個 fallback 狀態下的內容, 直到到達某個條件後才會渲染 default 狀態的正式內容, 通過使用
Suspense元件進行展示非同步渲染就更加的簡單。
具體的使用,我們可以用下面的例子來表示:
這是需要等待取值完成的的元件:

<template>
  <h1>{{ getData.result }}</h1></template><script>export default {
  name: "NewModel",
  async setup() {
    let getData = await new Promise((resolve) => {
      setTimeout(() => {
        return resolve({ result: "OK" });
      }, 3000);
    });
    return {
      getData,
    };
  },};</script>

在其他元件內呼叫它,當等待取值的元件取值完成後,會將loading狀態變為OK狀態

<template>
  <p>
    <suspense>
      <template #default>
        <NewSuspense></NewSuspense>
      </template>
      <template #fallback>
        <h1>Loadding...</h1>
      </template>
    </suspense>
  </p></template><script>import NewSuspense from "./suspens.vue";export default {
  name: "AppMain",
  components: {
    NewSuspense,
  },};</script><style></style>

效果如圖:
在這裡插入圖片描述

13.片段(Fragment)

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

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

但是在 Vue3.x 中,你可以直接寫多個根節點:

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

14.Tree-Shaking變化

Vue3.x 在考慮到 tree-shaking的基礎上重構了全域性和內部 API, 表現結果就是現在的全域性 API 需要通過 ES Module的參照方式進行具名參照, 比如在 Vue2.x 中,我們要使用 nextTick:

// vue2.ximport Vue from "vue"Vue.nextTick(()=>{
    ...})或者 
this.nextTick(()=>{
    ...})

Vue.nextTick() 是一個從 Vue 物件直接暴露出來的全域性 API,其實 $nextTick() 只是 Vue.nextTick() 的一個簡易包裝,只是為了方便而把後者的回撥函數的 this 繫結到了當前的範例。
在 Vue3.x 中改寫成這樣:

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

受影響的 API

這是一個比較大的變化, 因為以前的全域性 API 現在只能通過具名匯入,這一更改會對以下 API 有影響:

  1. Vue.nextTick
  2. Vue.observable(用 Vue.reactive 替換)
  3. Vue.version
  4. Vue.compile(僅限完整版本時可用)
  5. Vue.set(僅在 2.x 相容版本中可用)
  6. Vue.delete(與上同)

相關推薦:

以上就是整理分享經典技巧之vue3及與vue2的區別(部分)的詳細內容,更多請關注TW511.COM其它相關文章!