Vue 3 穩(wěn)定已經(jīng)有一段時(shí)間了。許多代碼庫(kù)正在生產(chǎn)中使用它,其他人最終也必須進(jìn)行遷移。我有機(jī)會(huì)與它一起工作,并記錄了我的錯(cuò)誤,這可能是你想避免的。
1.使用響應(yīng)式助手聲明基本類型
數(shù)據(jù)聲明曾經(jīng)很簡(jiǎn)單,但現(xiàn)在有多個(gè)輔助工具可用。現(xiàn)在的一般規(guī)則是:
- 使用 reactive 代替 Object, Array, Map, Set
- 使用 ref 代替 String, Number, Boolean
對(duì)于原始值使用響應(yīng)式會(huì)導(dǎo)致警告,并且該值不會(huì)被設(shè)置為響應(yīng)式:
/* DOES NOT WORK AS EXPECTED */
<script setup>
import { reactive } from "vue";
const count = reactive(0);
</script>
[Vue warn]: value cannot be made reactive
事例:https://codesandbox.io/s/jolly-ishizaka-ud946f?file=/src/App.vue
矛盾的是,反過(guò)來(lái)卻行得通!例如,使用 ref 聲明 Array 將在內(nèi)部調(diào)用 reactive 。
2.解構(gòu)失去響應(yīng)式值
讓我們想象一下,有一個(gè)具有計(jì)數(shù)器和一個(gè)按鈕以增加計(jì)數(shù)器的響應(yīng)式對(duì)象。
<template>
Counter: {{ state.count }}
<button @click="add">Increase</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({ count: 0 });
function add() {
state.count++;
}
return {
state,
add,
};
},
};
</script>
這個(gè)過(guò)程相當(dāng)直接,也能如預(yù)期般工作,但你可能會(huì)想利用 JAVAScript 的解構(gòu)特性來(lái)進(jìn)行下面的操作。
/* DOES NOT WORK AS EXPECTED */
<template>
<div>Counter: {{ count }}</div>
<button @click="add">Increase</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({ count: 0 });
function add() {
state.count++;
}
return {
...state,
add,
};
},
};
</script>
地址:https://codesandbox.io/s/gracious-ritchie-sfpswc?file=/src/App.vue
代碼看起來(lái)一樣,根據(jù)我們以前的經(jīng)驗(yàn),應(yīng)該可以運(yùn)行,但實(shí)際上,Vue 的反應(yīng)性跟蹤是基于屬性訪問(wèn)的。這意味著我們不能賦值或解構(gòu)一個(gè)響應(yīng)性對(duì)象,因?yàn)榕c第一個(gè)引用的響應(yīng)性連接會(huì)丟失。這是使用 reactive helper 的限制之一。
3.對(duì)".value"屬性感到困惑
使用 ref 的怪癖之一可能很難適應(yīng)。Ref 接受一個(gè)值并返回一個(gè)響應(yīng)式對(duì)象。該值在對(duì)象內(nèi)部在 .value 屬性下可用。
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
但是在模板中使用時(shí),引用會(huì)被解包, .value 不需要。
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }} // no .value needed
</button>
</template>
但請(qǐng)注意!解包(Unwrapping)只能在頂層屬性上有效。下面的代碼片段將產(chǎn)生 [object Object]。
// DON'T DO THIS
<script setup>
import { ref } from 'vue'
const object = { foo: ref(1) }
</script>
<template>
{{ object.foo + 1 }} // [object Object]
</template>
正確使用 ".value" 需要時(shí)間。盡管我偶爾會(huì)忘記它,但我發(fā)現(xiàn)我自己最初比需要的時(shí)候用得更頻繁。
4. Emitted Events
自 Vue 初始版本以來(lái),子組件可以使用 emits 與父組件通信。只需要添加一個(gè)自定義監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)事件即可。
this.$emit('my-event')
<my-component @my-event="doSomething" />
現(xiàn)在需要使用 defineEmits 宏來(lái)聲明emits。
<script setup>
const emit = defineEmits(['my-event'])
emit('my-event')
</script>
記住的另一件事是,無(wú)論是 defineEmits 還是 defineProps (用于聲明props),都不需要導(dǎo)入。當(dāng)使用 script setup. 時(shí),它們會(huì)自動(dòng)可用。
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup code
</script>
5.聲明額外選項(xiàng)
有一些 Options API 方法的屬性在 script setup 中不受支持。
- name
- inheritAttrs
- 插件或庫(kù)需要的自定義選項(xiàng)
解決方案是在同一組件中定義兩個(gè)不同的腳本,如腳本設(shè)置RFC中所定義的那樣:
<script>
export default {
name: 'CustomName',
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// script setup logic
</script>
6.使用 Reactivity Transform
響應(yīng)性轉(zhuǎn)換是 Vue 3 的一項(xiàng)實(shí)驗(yàn)性但有爭(zhēng)議的特性,其目標(biāo)是簡(jiǎn)化聲明組件的方式。這個(gè)想法是利用編譯時(shí)轉(zhuǎn)換來(lái)自動(dòng)解包 ref 并使 .value 變得過(guò)時(shí)。但現(xiàn)在已經(jīng)被取消,并將在 Vue 3.3 中被移除。它仍然會(huì)以一個(gè)包的形式存在,但由于它不是 Vue 核心的一部分,所以最好不要在它上面投入時(shí)間。
7. 定義異步組件
異步組件以前是通過(guò)將它們包含在一個(gè)函數(shù)中來(lái)聲明的。
const asyncModal = () => import('./Modal.vue')
自 Vue 3 開(kāi)始,異步組件需要使用 defineAsyncComponent 輔助函數(shù)進(jìn)行顯式定義:
import { defineAsyncComponent } from 'vue'
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
8. 在模板中使用不必要的包裝器
在Vue 2中,組件模板需要一個(gè)單一的根元素,這有時(shí)會(huì)引入不必要的包裝器:
<!-- Layout.vue -->
<template>
<div>
<header>...</header>
<mAIn>...</main>
<footer>...</footer>
</div>
</template>
這不再是問(wèn)題,因?yàn)楝F(xiàn)在支持多個(gè)根元素。
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
9. 使用錯(cuò)誤的生命周期事件。
所有組件生命周期事件都被重命名,要么通過(guò)添加 on 前綴,要么完全更改名稱。可以在以下圖形中檢查所有更改。
10. Skipping the Documentation
最后,官方文檔已經(jīng)進(jìn)行了重大改版,以反映新的 API,并包含許多有價(jià)值的注解、指南和最佳實(shí)踐。即使你是一名經(jīng)驗(yàn)豐富的 Vue 2 工程師,通過(guò)閱讀這個(gè)文檔,你肯定能學(xué)到一些新的東西。
每個(gè)框架都有一個(gè)學(xué)習(xí)曲線,Vue 3的曲線無(wú)疑比Vue 2更陡峭。我仍然不確定版本之間的遷移工作是否值得,但組合API更加清晰,一旦掌握了它,就會(huì)感覺(jué)很自然。