一、Vue3.3最新特性
Vue 3.3 “Rurouni Kenshin”——浪客劍心的最新版本主要目標(biāo)是優(yōu)化開發(fā)者的使用體驗(yàn)咏删,包括引入一些新的簡(jiǎn)化語法和宏惹想,以及在TypeScript方面的進(jìn)一步提升。
以下是主要的更新內(nèi)容:
1.為setup語法糖組件引入了泛型功能督函;
2.允許在組件文件中導(dǎo)入外部的ts類型嘀粱;
3.對(duì)defineEmits語法進(jìn)行了優(yōu)化;
4.新增defineSlots定義插槽類型辰狡;
5.新增defineModel來簡(jiǎn)化modelValue的語法锋叨;
6.Reactive Props 解構(gòu)
7.新增defineOptions定義組件名稱以及其他一些配置項(xiàng);
8.對(duì)toRef和toValue的功能進(jìn)行了增強(qiáng)宛篇。
接下來對(duì)這些改動(dòng)一一介紹
1.setup語法糖泛型
使用<script setup>的組件現(xiàn)在可以通過generic屬性接受泛型類型參數(shù)娃磺,一般情況下用不到,但有時(shí)候組件比較復(fù)雜時(shí)無法推斷類型的時(shí)候非常有用
<script setup lang="ts" generic="T">
import {defineProps} from "vue";
const props = defineProps<{
items: T[];
selected: T;
}>();
</script>
2.支持導(dǎo)入外部ts類型
defineProps和defineEmits支持使用import外部導(dǎo)入的類型聲明叫倍。
<script setup lang="ts">
import type { People } from './type.ts';
// 使用導(dǎo)入的類型 + 交集類型(導(dǎo)入類型基礎(chǔ)上增加一個(gè)字段)
defineProps< People & { extraProp?: string }>() //在vue3.3之前不支持使用import導(dǎo)入的類型
</script>
generic 的值與 TypeScript 中 <...> 之間的參數(shù)列表用法完全相同偷卧。例如,您可以使用多個(gè)參數(shù)段标、extend 約束涯冠、默認(rèn)類型和引用導(dǎo)入的類型:
<script setup lang="ts" generic="T extends string | number, U extends Item">
import type { Item } from './types'
defineProps<{
id: T
list: U[]
}>()
</script>
3.defineEmits語法優(yōu)化
之前defineEmits的類型參數(shù)只支持函數(shù)調(diào)用簽名語法:
// 以前
const emit = defineEmits<{
(e: 'foo', id: number): void
(e: 'bar', name: string, ...rest: any[]): void
}>()
// 或者不定義類型
const emit = defineEmits(['update:modelValue'])
在vue3.3中可以簡(jiǎn)化為以下寫法,更加簡(jiǎn)潔
// 現(xiàn)在
const emit = defineEmits<{
foo: [id: number]
bar: [name: string, ...rest: any[]]
}>()
在類型字面量中逼庞,key 是事件名稱,value 是事件參數(shù)的數(shù)組類型瞻赶。
以前的函數(shù)調(diào)用簽名語法仍然受支持赛糟。
4.新增defineSlots
新的defineSlots宏可以用來聲明插槽的類型,例如:
子組件DefineSlots
<script setup lang="ts">
defineSlots<{
default?: (props: { msg: string }) => any
item?: (props: { id: number }) => any
}>()
</script>
其中砸逊,defineSlots中的item就是我們定義的插槽名稱璧南,props就是我們定義的插槽的參數(shù),any就是我們定義插槽的返回值师逸,msg司倚、id就是插槽的參數(shù)。
父組件
<template>
<DefineSlots>
<template #default="{msg}" >{{msg}}</template>
<template #item="{id}" >{{id}}</template>
</DefineSlots>
</template>
<script setup lang="ts">
import DefineSlots from './components/defineSlots.vue';
</script>
插槽函數(shù)的返回類型目前被忽略篓像。
試驗(yàn)性功能:
5.新增defineModel
用于簡(jiǎn)化自定義v-model雙向綁定語法动知,在vue3.3中此功能是實(shí)驗(yàn)性的,需要明確的選擇加入员辩。
// vite.config.js
export default {
plugins: [
vue({
defineModel: true
})
]
}
以前組件想要支持 v-model盒粮,需要兩個(gè)步驟:
1.聲明 props
2.在打算更新 props 時(shí),emit update:propName 事件
子組件支持 v-model 的寫法:
<template>
<input :value="modelValue" @input="onInput" />
</template>
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
簡(jiǎn)化后的寫法
<template>
<input v-model="modelValue" />
</template>
<script setup>
const modelValue = defineModel()
// 也可以直接修改奠滑,等價(jià)于emit('update:modelValue', '新值')
// modelValue.value = '新的值'
</script>
6.Reactive Props 解構(gòu)
該功能可以解構(gòu)的 props 并保持響應(yīng)性丹皱,并提供了一種更符合人體工程學(xué)的方式來聲明 props 的默認(rèn)值:
<script setup>
import { watchEffect } from 'vue'
const { msg = 'hello' } = defineProps(['msg'])
watchEffect(() => {
// 在 watch 和 computed 中使用 msg
// 能夠正常收集依賴妒穴,就好像使用 props.msg
console.log(`msg is: ${msg}`)
})
</script>
<template>{{ msg }}</template>
此功能是實(shí)驗(yàn)性的,需要明確的選擇加入摊崭。
// vite.config.js
export default {
plugins: [
vue({
propsDestructure: true
})
]
}
其它值得注意的功能:
7.新增 defineOptions
新的defineOptions宏允許直接在<script setup>中聲明組件選項(xiàng)讼油,而不需要單獨(dú)的<script>塊
當(dāng)我們想使用 mounted
鉤子函數(shù)時(shí),會(huì)報(bào)錯(cuò)呢簸,因?yàn)?<script setup>
會(huì)將所有的代碼都放在 setup
函數(shù)中矮台,而 mounted
是在 setup
函數(shù)之后執(zhí)行的,所以會(huì)報(bào)錯(cuò)阔墩。
此外某些場(chǎng)景緩存頁面數(shù)據(jù)嘿架,可能需要設(shè)置組件名稱 name。
可以用 defineOptions 定義任意選項(xiàng)啸箫,但 props, emits, expose, slots 除外
<script setup>
import { onMounted } from 'vue';
// eslint-disable-next-line no-undef
defineOptions({
name: 'DefineOptions',
inheritAttrs: false,
mounted() {
console.log('mounted');
}
})
onMounted(()=>{
console.log('onMounted')
})
</script>
8.toRef和toValue增強(qiáng)
toRef已得到增強(qiáng)耸彪,將元素變成響應(yīng)式,支持將值/getter/現(xiàn)有refs規(guī)范化為refs:
// 等價(jià)于ref(1)
toRef(1)
// 創(chuàng)建一個(gè)readonly ref忘苛,在.value訪問時(shí)調(diào)用getter
toRef(() => props.foo)
// 按原樣返回現(xiàn)有的引用
toRef(existingRef)
使用getter調(diào)用toRef類似于computed蝉娜,但當(dāng)getter只是執(zhí)行屬性訪問而沒有昂貴的計(jì)算時(shí),效率會(huì)更高扎唾。
新的toValue實(shí)用程序方法提供了相反的功能召川,將values / getters / refs標(biāo)準(zhǔn)化為值:
toValue(1) // --> 1
toValue(ref(1)) // --> 1
toValue(() => 1) // --> 1
toValue可以在composable中代替unref使用,這樣你的composable就可以接受getter作為反應(yīng)式數(shù)據(jù)源:
// 以前:分配不必要的中間引用
useFeature(computed(() => props.foo))
useFeature(toRef(props, 'foo'))
// 現(xiàn)在:更高效和簡(jiǎn)潔
useFeature(() => props.foo)
toRef和toValue之間的關(guān)系類似于ref和unref之間的關(guān)系胸遇,主要區(qū)別在于對(duì)getter函數(shù)的特殊處理荧呐。
9.依賴更新
依賴更新
升級(jí)到 3.3 時(shí),建議同時(shí)更新以下依賴項(xiàng):
volar / vue-tsc@^1.6.4
vite@^4.3.5
@vitejs/plugin-vue@^4.2.0
vue-loader@^17.1.0(如果使用 webpack 或 vue-cli)
二纸镊、Vue Router新特性
在使用vue-router4中params 進(jìn)行路由組件之間傳參
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
router.push({ name: 'first', params })
}
</script>
在接收頁面嘗試渲染params傳遞的數(shù)據(jù):
template>
<div>姓名:{{ route.params?.name }}</div>
<div>電話:{{ route.params?.phone }}</div>
<div>年齡:{{ route.params?.age }}</div>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
跳轉(zhuǎn)頁面接收不了并出現(xiàn)如下Vue Router警告:
我們點(diǎn)擊連接后發(fā)現(xiàn)了原因:
也就是說倍阐,從Vue Router的2022-8-22 這次更新后,我們使用上面的方式在新頁面無法獲取逗威。
Vue也給我們提出了代替方案:
1.使用query的方式傳參
修改為query方式傳參數(shù)峰搪,注意query方式只能用路由表中的path,不是name凯旭,并且所有的參數(shù)都會(huì)顯示在URL地址上概耻。
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
// query方式
router.push({ path: '/first', query: params })
}
</script>
2.將數(shù)據(jù)放在pinia或者vuex這樣的狀態(tài)管理庫里面
實(shí)際工作中,咱們非必要不會(huì)使用這種方式罐呼。
3.使用動(dòng)態(tài)路由匹配
如果傳遞參數(shù)較少的情況下鞠柄,可以嘗試使用動(dòng)態(tài)路由匹配方式,只要修改一下path定義部分就可以了:
{
// path: '/first',
path: '/first/:id/:name/:phone/:age', // 動(dòng)態(tài)路由弄贿,修改一下path定義
name: 'first',
component: () => import('../views/FirstView.vue')
}
跳轉(zhuǎn)頁面通過path春锋,接收頁面使用route.params
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
function toFirst() {
// 動(dòng)態(tài)路由
router.push({ path: '/first/1/wjj/123456789/23'})
}
</script>
<!--動(dòng)態(tài)路由方式-->
<p>姓名:{{ route.params?.name }}</p>
<p>電話:{{ route.params?.phone }}</p>
<p>年齡:{{ route.params?.age }}</p>
注意:如果使用了動(dòng)態(tài)路由匹配方式,path中的每個(gè)參數(shù)都是必須傳遞差凹,否則會(huì)報(bào)錯(cuò)期奔。個(gè)人覺得動(dòng)態(tài)路由匹配方式接收參數(shù)與params方式一樣侧馅,如果不把params參數(shù)寫在路由路徑上無法得到params參數(shù),雖然不算棄用了params呐萌,但是每次都把params參數(shù)都寫在路由路徑上也是非常麻煩的一件事馁痴。
4.使用HistoryAPI的方式
在跳轉(zhuǎn)頁面使用state參數(shù)
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
// state
router.push({ name: 'first', state: { params } })
}
</script>
跳轉(zhuǎn)后的頁面接收:
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
const historyParams = history.state.params
</script>