Vue3.0的新特性有哪些呢,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
我們提供的服務(wù)有:網(wǎng)站設(shè)計制作、網(wǎng)站制作、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、舒城ssl等。為近1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的舒城網(wǎng)站制作公司
尤大大在B站直播時分享了Vue3.0的幾個亮點(diǎn):
Performance:性能優(yōu)化
Tree-shaking support:支持搖樹優(yōu)化
Composition API:組合API
Fragment,Teleport,Suspense:新增的組件
Better TypeScript support:更好的TypeScript支持
Custom Renderer API:自定義渲染器
在性能方面,對比Vue2.x,性能提升了1.3~2倍左右;打包后的體積也更小了,如果單單寫一個HelloWorld進(jìn)行打包,只有13.5kb;加上所有運(yùn)行時特性,也不過22.5kb。
那么作為終端用戶的我們,在開發(fā)時,和Vue2.x有什么不同呢?Talk is cheap
,我們還是來看代碼。
Vue3最重要的變化之一就是引入了Tree-Shaking,Tree-Shaking帶來的bundle體積更小是顯而易見的。在2.x版本中,很多函數(shù)都掛載在全局Vue對象上,比如nextTick、nextTick、nextTick、set等函數(shù),因此雖然我們可能用不到,但打包時只要引入了vue這些全局函數(shù)仍然會打包進(jìn)bundle中。
而在Vue3中,所有的API都通過ES6模塊化的方式引入,這樣就能讓webpack或rollup等打包工具在打包時對沒有用到API進(jìn)行剔除,最小化bundle體積;我們在main.js中就能發(fā)現(xiàn)這樣的變化:
//src/main.js import { createApp } from "vue"; import App from "./App.vue"; import router from "./router"; const app = createApp(App); app.use(router).mount("#app");
創(chuàng)建app實(shí)例方式從原來的new Vue()
變?yōu)橥ㄟ^createApp函數(shù)進(jìn)行創(chuàng)建;不過一些核心的功能比如virtualDOM更新算法和響應(yīng)式系統(tǒng)無論如何都是會被打包的;這樣帶來的變化就是以前在全局配置的組件(Vue.component)、指令(Vue.directive)、混入(Vue.mixin)和插件(Vue.use)等變?yōu)橹苯訏燧d在實(shí)例上的方法;我們通過創(chuàng)建的實(shí)例來調(diào)用,帶來的好處就是一個應(yīng)用可以有多個Vue實(shí)例,不同實(shí)例之間的配置也不會相互影響:
const app = createApp(App) app.use(/* ... */) app.mixin(/* ... */) app.component(/* ... */) app.directive(/* ... */)
因此Vue2.x的以下全局API也需要改為ES6模塊化引入:
Vue.nextTick
Vue.observable不再支持,改為reactive
Vue.version
Vue.compile (僅全構(gòu)建)
Vue.set (僅兼容構(gòu)建)
Vue.delete (僅兼容構(gòu)建)
除此之外,vuex和vue-router也都使用了Tree-Shaking進(jìn)行了改進(jìn),不過api的語法改動不大:
//src/store/index.js import { createStore } from "vuex"; export default createStore({ state: {}, mutations: {}, actions: {}, modules: {}, }); //src/router/index.js import { createRouter, createWebHistory } from "vue-router"; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, });
我們都知道,在Vue2.x中有8個生命周期函數(shù):
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
在vue3中,新增了一個setup
生命周期函數(shù),setup執(zhí)行的時機(jī)是在beforeCreate
生命函數(shù)之前執(zhí)行,因此在這個函數(shù)中是不能通過this
來獲取實(shí)例的;同時為了命名的統(tǒng)一,將beforeDestroy
改名為beforeUnmount
,destroyed
改名為unmounted
,因此vue3有以下生命周期函數(shù):
beforeCreate(建議使用setup代替)
created(建議使用setup代替)
setup
beforeMount
mounted
beforeUpdate
updated
beforeUnmount
unmounted
同時,vue3新增了生命周期鉤子,我們可以通過在生命周期函數(shù)前加on
來訪問組件的生命周期,我們可以使用以下生命周期鉤子:
onBeforeMount
onMounted
onBeforeUpdate
onUpdated
onBeforeUnmount
onUnmounted
onErrorCaptured
onRenderTracked
onRenderTriggered
那么這些鉤子函數(shù)如何來進(jìn)行調(diào)用呢?我們在setup中掛載生命周期鉤子,當(dāng)執(zhí)行到對應(yīng)的生命周期時,就調(diào)用對應(yīng)的鉤子函數(shù):
import { onBeforeMount, onMounted } from "vue"; export default { setup() { console.log("----setup----"); onBeforeMount(() => { // beforeMount代碼執(zhí)行 }); onMounted(() => { // mounted代碼執(zhí)行 }); }, }
說完生命周期,下面就是我們期待的Vue3新增加的那些功能。
我們可以使用reactive
來為JS對象創(chuàng)建響應(yīng)式狀態(tài):
import { reactive, toRefs } from "vue"; const user = reactive({ name: 'Vue2', age: 18, }); user.name = 'Vue3'
reactive相當(dāng)于Vue2.x中的Vue.observable
。
reactive函數(shù)只接收object和array等復(fù)雜數(shù)據(jù)類型。
對于一些基本數(shù)據(jù)類型,比如字符串和數(shù)值等,我們想要讓它變成響應(yīng)式,我們當(dāng)然也可以通過reactive函數(shù)創(chuàng)建對象的方式,但是Vue3提供了另一個函數(shù)ref
:
import { ref } from "vue"; const num = ref(0); const str = ref(""); const male = ref(true); num.value++; console.log(num.value); str.value = "new val"; console.log(str.value); male.value = false; console.log(male.value);
ref返回的響應(yīng)式對象是只包含一個名為value參數(shù)的RefImpl對象,在js中獲取和修改都是通過它的value屬性;但是在模板中被渲染時,自動展開內(nèi)部的值,因此不需要在模板中追加.value
。
<template> <p> <span>{{ count }}</span> <button @click="count ++">Increment count</button> </p> </template> <script> import { ref } from 'vue' export default { setup() { const count = ref(0) return { count } } } </script>
reactive主要負(fù)責(zé)復(fù)雜數(shù)據(jù)結(jié)構(gòu),而ref主要處理基本數(shù)據(jù)結(jié)構(gòu);但是很多童鞋就會誤解ref只能處理基本數(shù)據(jù),ref本身也是能處理對象和數(shù)組的:
import { ref } from "vue"; const obj = ref({ name: "qwe", age: 1, }); setTimeout(() => { obj.value.name = "asd"; }, 1000); const list = ref([1, 2, 3, 4, 6]); setTimeout(() => { list.value.push(7); }, 2000);
當(dāng)我們處理一些大型響應(yīng)式對象的property時,我們很希望使用ES6的解構(gòu)來獲取我們想要的值:
let book = reactive({ name: 'Learn Vue', year: 2020, title: 'Chapter one' }) let { name, } = book name = 'new Learn' // Learn Vue console.log(book.name);
但是很遺憾,這樣會消除它的響應(yīng)式;對于這種情況,我們可以將響應(yīng)式對象轉(zhuǎn)換為一組ref,這些ref將保留與源對象的響應(yīng)式關(guān)聯(lián):
let book = reactive({ name: 'Learn Vue', year: 2020, title: 'Chapter one' }) let { name, } = toRefs(book) // 注意這里解構(gòu)出來的name是ref對象 // 需要通過value來取值賦值 name.value = 'new Learn' // new Learn console.log(book.name);
對于一些只讀數(shù)據(jù),我們希望防止它發(fā)生任何改變,可以通過readonly
來創(chuàng)建一個只讀的對象:
import { reactive, readonly } from "vue"; let book = reactive({ name: 'Learn Vue', year: 2020, title: 'Chapter one' }) const copy = readonly(book); //Set operation on key "name" failed: target is readonly. copy.name = "new copy";
有時我們需要的值依賴于其他值的狀態(tài),在vue2.x中我們使用computed函數(shù)
來進(jìn)行計算屬性,在vue3中將computed功能進(jìn)行了抽離,它接受一個getter函數(shù),并為getter返回的值創(chuàng)建了一個不可變的響應(yīng)式ref對象:
const num = ref(0); const double = computed(() => num.value * 2); num.value++; // 2 console.log(double.value); // Warning: computed value is readonly double.value = 4
或者我們也可以使用get和set函數(shù)創(chuàng)建一個可讀寫的ref對象:
const num = ref(0); const double = computed({ get: () => num.value * 2, set: (val) => (num.value = val / 2), }); num.value++; // 2 console.log(double.value); double.value = 8 // 4 console.log(num.value);
和computed相對應(yīng)的就是watch,computed是多對一的關(guān)系,而watch則是一對多的關(guān)系;vue3也提供了兩個函數(shù)來偵聽數(shù)據(jù)源的變化:watch和watchEffect。
我們先來看下watch,它的用法和組件的watch選項(xiàng)用法完全相同,它需要監(jiān)聽某個數(shù)據(jù)源,然后執(zhí)行具體的回調(diào)函數(shù),我們首先看下它監(jiān)聽單個數(shù)據(jù)源的用法:
import { reactive, ref, watch } from "vue"; const state = reactive({ count: 0, }); //偵聽時返回值得getter函數(shù) watch( () => state.count, (count, prevCount) => { // 1 0 console.log(count, prevCount); } ); state.count++; const count = ref(0); //直接偵聽ref watch(count, (count, prevCount) => { // 2 0 console.log(count, prevCount, "watch"); }); count.value = 2;
我們也可以把多個值放在一個數(shù)組中進(jìn)行偵聽,最后的值也以數(shù)組形式返回:
const state = reactive({ count: 1, }); const count = ref(2); watch([() => state.count, count], (newVal, oldVal) => { //[3, 2] [1, 2] //[3, 4] [3, 2] console.log(newVal, oldVal); }); state.count = 3; count.value = 4;
如果我們來偵聽一個深度嵌套的對象屬性變化時,需要設(shè)置deep:true
:
const deepObj = reactive({ a: { b: { c: "hello", }, }, }); watch( () => deepObj, (val, old) => { // new hello new hello console.log(val.a.b.c, old.a.b.c); }, { deep: true } ); deepObj.a.b.c = "new hello";
最后的打印結(jié)果可以發(fā)現(xiàn)都是改變后的值,這是因?yàn)閭陕犚粋€響應(yīng)式對象始終返回該對象的引用,因此我們需要對值進(jìn)行深拷貝:
import _ from "lodash"; const deepObj = reactive({ a: { b: { c: "hello", }, }, }); watch( () => _.cloneDeep(deepObj), (val, old) => { // new hello hello console.log(val.a.b.c, old.a.b.c); }, { deep: true } ); deepObj.a.b.c = "new hello";
一般偵聽都會在組件銷毀時自動停止,但是有時候我們想在組件銷毀前手動的方式進(jìn)行停止,可以調(diào)用watch返回的stop函數(shù)進(jìn)行停止:
const count = ref(0); const stop = watch(count, (count, prevCount) => { // 不執(zhí)行 console.log(count, prevCount); }); setTimeout(()=>{ count.value = 2; }, 1000); // 停止watch stop();
還有一個函數(shù)watchEffect也可以用來進(jìn)行偵聽,但是都已經(jīng)有watch了,這個watchEffect和watch有什么區(qū)別呢?他們的用法主要有以下幾點(diǎn)不同:
watchEffect不需要手動傳入依賴
每次初始化時watchEffect都會執(zhí)行一次回調(diào)函數(shù)來自動獲取依賴
watchEffect無法獲取到原值,只能得到變化后的值
import { reactive, ref, watch, watchEffect } from "vue"; const count = ref(0); const state = reactive({ year: 2021, }); watchEffect(() => { console.log(count.value); console.log(state.year); }); setInterval(() => { count.value++; state.year++; }, 1000);
watchEffect會在頁面加載時自動執(zhí)行一次,追蹤響應(yīng)式依賴;在加載后定時器每隔1s執(zhí)行時,watchEffect都會監(jiān)聽到數(shù)據(jù)的變化自動執(zhí)行,每次執(zhí)行都是獲取到變化后的值。
Composition API(組合API)也是Vue3中最重要的一個功能了,之前的2.x版本采用的是Options API
(選項(xiàng)API),即官方定義好了寫法:data、computed、methods,需要在哪里寫就在哪里寫,這樣帶來的問題就是隨著功能增加,代碼也越來復(fù)雜,我們看代碼需要上下反復(fù)橫跳:
Composition API對比
上圖中,一種顏色代表一個功能,我們可以看到
Options API
的功能代碼比較分散;Composition API
則可以將同一個功能的邏輯,組織在一個函數(shù)內(nèi)部,利于維護(hù)。
我們首先來看下之前Options API的寫法:
export default { components: {}, data() {}, computed: {}, watch: {}, mounted() {}, }
Options API
就是將同一類型的東西放在同一個選項(xiàng)中,當(dāng)我們的數(shù)據(jù)比較少的時候,這樣的組織方式是比較清晰的;但是隨著數(shù)據(jù)增多,我們維護(hù)的功能點(diǎn)會涉及到多個data和methods,但是我們無法感知哪些data和methods是需要涉及到的,經(jīng)常需要來回切換查找,甚至是需要理解其他功能的邏輯,這也導(dǎo)致了組件難以理解和閱讀。
而Composition API
做的就是把同一功能的代碼放到一起維護(hù),這樣我們需要維護(hù)一個功能點(diǎn)的時候,不用去關(guān)心其他的邏輯,只關(guān)注當(dāng)前的功能;Composition API
通過setup
選項(xiàng)來組織代碼:
export default { setup(props, context) {} };
我們看到這里它接收了兩個參數(shù)props和context,props就是父組件傳入的一些數(shù)據(jù),context是一個上下文對象,是從2.x暴露出來的一些屬性:
attrs
slots
emit
注:props的數(shù)據(jù)也需要通過toRefs解構(gòu),否則響應(yīng)式數(shù)據(jù)會失效。
我們通過一個Button按鈕來看下setup具體的用法:
舉個栗子
<template> <p>{{ state.count }} * 2 = {{ double }}</p> <p>{{ num }}</p> <p @click="add">Add</p> </template> <script> import { reactive, computed, ref } from "vue"; export default { name: "Button", setup() { const state = reactive({ count: 1, }); const num = ref(2); function add() { state.count++; num.value += 10; } const double = computed(() => state.count * 2); return { state, double, num, add, }; }, }; </script>
很多童鞋可能就有疑惑了,這跟我在data和methods中寫沒什么區(qū)別么,不就是把他們放到一起么?我們可以將setup
中的功能進(jìn)行提取分割成一個一個獨(dú)立函數(shù),每個函數(shù)還可以在不同的組件中進(jìn)行邏輯復(fù)用:
export default { setup() { const { networkState } = useNetworkState(); const { user } = userDeatil(); const { list } = tableData(); return { networkState, user, list, }; }, }; function useNetworkState() {} function userDeatil() {} function tableData() {}
所謂的Fragment,就是片段;在vue2.x中,要求每個模板必須有一個根節(jié)點(diǎn),所以我們代碼要這樣寫:
<template> <p> <span></span> <span></span> </p> </template>
或者在Vue2.x中還可以引入vue-fragments
庫,用一個虛擬的fragment代替p;在React中,解決方法是通過的一個React.Fragment
標(biāo)簽創(chuàng)建一個虛擬元素;在Vue3中我們可以直接不需要根節(jié)點(diǎn):
<template> <span>hello</span> <span>world</span> </template>
這樣就少了很多沒有意義的p元素。
Teleport翻譯過來就是傳送、遠(yuǎn)距離傳送的意思;顧名思義,它可以將插槽中的元素或者組件傳送到頁面的其他位置:
傳送門游戲
在React中可以通過createPortal
函數(shù)來創(chuàng)建需要傳送的節(jié)點(diǎn);本來尤大大想起名叫Portal
,但是H5原生的Portal標(biāo)簽
也在計劃中,雖然有一些安全問題,但是為了避免重名,因此改成Teleport
。
Teleport一個常見的使用場景,就是在一些嵌套比較深的組件來轉(zhuǎn)移模態(tài)框的位置。雖然在邏輯上模態(tài)框是屬于該組件的,但是在樣式和DOM結(jié)構(gòu)上,嵌套層級后較深后不利于進(jìn)行維護(hù)(z-index等問題);因此我們需要將其進(jìn)行剝離出來:
<template> <button @click="showDialog = true">打開模態(tài)框</button> <teleport to="body"> <p class="modal" v-if="showDialog" style="position: fixed"> 我是一個模態(tài)框 <button @click="showDialog = false">關(guān)閉</button> <child-component :msg="msg"></child-component> </p> </teleport> </template> <script> export default { data() { return { showDialog: false, msg: "hello" }; }, }; </script>
這里的Teleport中的modal p就被傳送到了body的底部;雖然在不同的地方進(jìn)行渲染,但是Teleport中的元素和組件還是屬于父組件的邏輯子組件,還是可以和父組件進(jìn)行數(shù)據(jù)通信。Teleport接收兩個參數(shù)to
和disabled
:
to - string:必須是有效的查詢選擇器或 HTMLElement,可以id或者class選擇器等。
disabled - boolean:如果是true表示禁用teleport的功能,其插槽內(nèi)容將不會移動到任何位置,默認(rèn)false不禁用。
Suspense是Vue3推出的一個內(nèi)置組件,它允許我們的程序在等待異步組件時渲染一些后備的內(nèi)容,可以讓我們創(chuàng)建一個平滑的用戶體驗(yàn);Vue中加載異步組件其實(shí)在Vue2.x中已經(jīng)有了,我們用的vue-router中加載的路由組件其實(shí)也是一個異步組件:
export default { name: "Home", components: { AsyncButton: () => import("../components/AsyncButton"), }, }
在Vue3中重新定義,異步組件需要通過defineAsyncComponent
來進(jìn)行顯示的定義:
// 全局定義異步組件 //src/main.js import { defineAsyncComponent } from "vue"; const AsyncButton = defineAsyncComponent(() => import("./components/AsyncButton.vue") ); app.component("AsyncButton", AsyncButton); // 組件內(nèi)定義異步組件 // src/views/Home.vue import { defineAsyncComponent } from "vue"; export default { components: { AsyncButton: defineAsyncComponent(() => import("../components/AsyncButton") ), }, };
同時對異步組件的可以進(jìn)行更精細(xì)的管理:
export default { components: { AsyncButton: defineAsyncComponent({ delay: 100, timeout: 3000, loader: () => import("../components/AsyncButton"), errorComponent: ErrorComponent, onError(error, retry, fail, attempts) { if (attempts <= 3) { retry(); } else { fail(); } }, }), }, };
這樣我們對異步組件加載情況就能掌控,在加載失敗也能重新加載或者展示異常的狀態(tài):
異步組件加載失敗
我們回到Suspense,上面說到它主要是在組件加載時渲染一些后備的內(nèi)容,它提供了兩個slot插槽,一個default
默認(rèn),一個fallback
加載中的狀態(tài):
<template> <p> <button @click="showButton">展示異步組件</button> <template v-if="isShowButton"> <Suspense> <template #default> <AsyncButton></AsyncButton> </template> <template #fallback> <p>組件加載中...</p> </template> </Suspense> </template> </p> </template> <script> export default { setup() { const isShowButton = ref(false); function showButton() { isShowButton.value = true; } return { isShowButton, showButton, }; }, } </script>
異步組件加載顯示占位
非兼容的功能主要是一些和Vue2.x版本改動較大的語法,已經(jīng)在Vue3上可能存在兼容問題了。
在Vue2.x中,我們可以定義data為object
或者function
,但是我們知道在組件中如果data是object的話會出現(xiàn)數(shù)據(jù)互相影響,因?yàn)閛bject是引用數(shù)據(jù)類型;
在Vue3中,data只接受function
類型,通過function
返回對象;同時Mixin
的合并行為也發(fā)生了改變,當(dāng)mixin和基類中data合并時,會執(zhí)行淺拷貝合并:
const Mixin = { data() { return { user: { name: 'Jack', id: 1, address: { prov: 2, city: 3, }, } } } } const Component = { mixins: [Mixin], data() { return { user: { id: 2, address: { prov: 4, }, } } } } // vue2結(jié)果: { id: 2, name: 'Jack', address: { prov: 4, city: 3 } } // vue3結(jié)果: user: { id: 2, address: { prov: 4, }, }
我們看到最后合并的結(jié)果,vue2.x會進(jìn)行深拷貝,對data中的數(shù)據(jù)向下深入合并拷貝;而vue3只進(jìn)行淺層拷貝,對data中數(shù)據(jù)發(fā)現(xiàn)已存在就不合并拷貝。
在vue2.x中,我們還可以通過過濾器filter
來處理一些文本內(nèi)容的展示:
<template> <p>{{ status | statusText }}</p> </template> <script> export default { props: { status: { type: Number, default: 1 } }, filters: { statusText(value){ if(value === 1){ return '訂單未下單' } else if(value === 2){ return '訂單待支付' } else if(value === 3){ return '訂單已完成' } } } } </script>
最常見的就是處理一些訂單的文案展示等;然而在vue3中,過濾器filter已經(jīng)刪除,不再支持了,官方建議使用方法調(diào)用或者計算屬性computed
來進(jìn)行代替。
在Vue2.x中,v-model
相當(dāng)于綁定value
屬性和input
事件,它本質(zhì)也是一個語法糖:
<child-component v-model="msg"></child-component> <!-- 相當(dāng)于 --> <child-component :value="msg" @input="msg=$event"></child-component>
在某些情況下我們需要對多個值進(jìn)行雙向綁定,其他的值就需要顯示的使用回調(diào)函數(shù)來改變了:
<child-component v-model="msg" :msg1="msg1" @change1="msg1=$event" :msg2="msg2" @change2="msg2=$event"> </child-component>
在vue2.3.0+版本引入了.sync
修飾符,其本質(zhì)也是語法糖,是在組件上綁定@update:propName
回調(diào),語法更簡潔:
<child-component :msg1.sync="msg1" :msg2.sync="msg2"> </child-component> <!-- 相當(dāng)于 --> <child-component :msg1="msg1" @update:msg1="msg1=$event" :msg2="msg2" @update:msg2="msg2=$event"> </child-component>
Vue3中將v-model
和.sync
進(jìn)行了功能的整合,拋棄了.sync,表示:多個雙向綁定value值直接用多個v-model傳就好了;同時也將v-model默認(rèn)傳的prop名稱由value改成了modelValue:
<child-component v-model="msg"> </child-component> <!-- 相當(dāng)于 --> <child-component :modelValue="msg" @update:modelValue="msg = $event"> </child-component>
如果我們想通過v-model傳遞多個值,可以將一個argument
傳遞給v-model:
<child-component v-model.msg1="msg1" v-model.msg2="msg2"> </child-component> <!-- 相當(dāng)于 --> <child-component :msg1="msg1" @update:msg1="msg1=$event" :msg2="msg2" @update:msg2="msg2=$event"> </child-component>
在Vue2.x中,我們都知道v-for每次循環(huán)都需要給每個子節(jié)點(diǎn)一個唯一的key,還不能綁定在template標(biāo)簽上,
<template v-for="item in list"> <p :key="item.id">...</p> <span :key="item.id">...</span> </template>
而在Vue3中,key值應(yīng)該被放置在template標(biāo)簽上,這樣我們就不用為每個子節(jié)點(diǎn)設(shè)一遍:
<template v-for="item in list" :key="item.id"> <p>...</p> <span>...</span> </template>
在vue2.x中,如果一個元素同時定義了v-bind="object"
和一個相同的單獨(dú)的屬性,那么這個單獨(dú)的屬性會覆蓋object
中的綁定:
<p id="red" v-bind="{ id: 'blue' }"></p> <p v-bind="{ id: 'blue' }" id="red"></p> <!-- 最后結(jié)果都相同 --> <p id="red"></p>
然而在vue3中,如果一個元素同時定義了v-bind="object"
和一個相同的單獨(dú)的屬性,那么聲明綁定的順序決定了最后的結(jié)果(后者覆蓋前者):
<!-- template --> <p id="red" v-bind="{ id: 'blue' }"></p> <!-- result --> <p id="blue"></p> <!-- template --> <p v-bind="{ id: 'blue' }" id="red"></p> <!-- result --> <p id="red"></p>
vue2.x中,在v-for上使用ref
屬性,通過this.$refs
會得到一個數(shù)組:
<template <p v-for="item in list" :ref="setItemRef"></p> </template> <script> export default { data(){ list: [1, 2] }, mounted () { // [p, p] console.log(this.$refs.setItemRef) } } </script>
但是這樣可能不是我們想要的結(jié)果;因此vue3不再自動創(chuàng)建數(shù)組,而是將ref的處理方式變?yōu)榱撕瘮?shù),該函數(shù)默認(rèn)傳入該節(jié)點(diǎn):
<template <p v-for="item in 3" :ref="setItemRef"></p> </template> <script> import { reactive, onUpdated } from 'vue' export default { setup() { let itemRefs = reactive([]) const setItemRef = el => { itemRefs.push(el) } onUpdated(() => { console.log(itemRefs) }) return { itemRefs, setItemRef } } } </script>
在vue2.x中,在一個元素上同時使用v-for和v-if,v-for
有更高的優(yōu)先級,因此在vue2.x中做性能優(yōu)化,有一個重要的點(diǎn)就是v-for和v-if不能放在同一個元素上。
而在vue3中,v-if
比v-for
有更高的優(yōu)先級。因此下面的代碼,在vue2.x中能正常運(yùn)行,但是在vue3中v-if生效時并沒有item
變量,因此會報錯:
<template> <p v-for="item in list" v-if="item % 2 === 0" :key="item">{{ item }}</p> </template> <script> export default { data() { return { list: [1, 2, 3, 4, 5], }; }, }; </script>
關(guān)于Vue3.0的新特性有哪些呢問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。
名稱欄目:Vue3.0的新特性有哪些呢
當(dāng)前路徑:http://jinyejixie.com/article42/ijdiec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、做網(wǎng)站、網(wǎng)站導(dǎo)航、電子商務(wù)、微信小程序、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)