1. 【Vuejs】1117- 全面總結(jié)Vue3.0的新特性

        共 10117字,需瀏覽 21分鐘

         ·

        2021-10-22 04:24



        總字?jǐn)?shù):5,864 | 閱讀時間:26分鐘 


        Vue3.0從20年九月發(fā)布第一個One Piece版本,到現(xiàn)在一直在更新優(yōu)化;除了服務(wù)端渲染的工作,其他工作已經(jīng)大部分完成了,中文版的官方文檔也已經(jīng)放出;那么作為終端用戶的我們來看下Vue3新增了哪些功能和特性。

        尤大大在B站直播時分享了Vue3.0的幾個亮點:

        • 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,我們還是來看代碼。

        Tree-shaking

        Vue3最重要的變化之一就是引入了Tree-Shaking,Tree-Shaking帶來的bundle體積更小是顯而易見的。在2.x版本中,很多函數(shù)都掛載在全局Vue對象上,比如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實例方式從原來的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在實例上的方法;我們通過創(chuàng)建的實例來調(diào)用,帶來的好處就是一個應(yīng)用可以有多個Vue實例,不同實例之間的配置也不會相互影響:

        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,
        });

        更多關(guān)于Tree-Shaking的使用可以在Webpack配置全解析中查看。

        生命周期函數(shù)

        我們都知道,在Vue2.x中有8個生命周期函數(shù):

        • beforeCreate
        • created
        • beforeMount
        • mounted
        • beforeUpdate
        • updated
        • beforeDestroy
        • destroyed

        在vue3中,新增了一個setup生命周期函數(shù),setup執(zhí)行的時機(jī)是在beforeCreate生命函數(shù)之前執(zhí)行,因此在這個函數(shù)中是不能通過this來獲取實例的;同時為了命名的統(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新增加的那些功能。

        響應(yīng)式API

        我們在深入學(xué)習(xí)Object.defineProperty和Proxy講解過Proxy優(yōu)點以及Vue3為什么改用Proxy實現(xiàn)響應(yīng)式,同時Vue3也將一些響應(yīng)式的API進(jìn)行抽離,以便代碼更好的復(fù)用。

        我們可以使用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。




        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);

        響應(yīng)式偵聽

        和computed相對應(yīng)的就是watch,computed是多對一的關(guān)系,而watch則是一對多的關(guān)系;vue3也提供了兩個函數(shù)來偵聽數(shù)據(jù)源的變化:watch和watchEffect。

        我們先來看下watch,它的用法和組件的watch選項用法完全相同,它需要監(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īng)式對象始終返回該對象的引用,因此我們需要對值進(jìn)行深拷貝:

        import?_?from?"lodash";
        const?deepObj?=?reactive({
        ??a:?{
        ????b:?{
        ??????c:?"hello",
        ????},
        ??},
        });

        watch(
        ??()?=>?_.cloneDeep(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";

        一般偵聽都會在組件銷毀時自動停止,但是有時候我們想在組件銷毀前手動的方式進(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ū)別呢?他們的用法主要有以下幾點不同:

        1. watchEffect不需要手動傳入依賴
        2. 每次初始化時watchEffect都會執(zhí)行一次回調(diào)函數(shù)來自動獲取依賴
        3. 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í)行都是獲取到變化后的值。

        組合API

        Composition API(組合API)也是Vue3中最重要的一個功能了,之前的2.x版本采用的是Options API(選項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就是將同一類型的東西放在同一個選項中,當(dāng)我們的數(shù)據(jù)比較少的時候,這樣的組織方式是比較清晰的;但是隨著數(shù)據(jù)增多,我們維護(hù)的功能點會涉及到多個data和methods,但是我們無法感知哪些data和methods是需要涉及到的,經(jīng)常需要來回切換查找,甚至是需要理解其他功能的邏輯,這也導(dǎo)致了組件難以理解和閱讀。

        Composition API做的就是把同一功能的代碼放到一起維護(hù),這樣我們需要維護(hù)一個功能點的時候,不用去關(guān)心其他的邏輯,只關(guān)注當(dāng)前的功能;Composition API通過setup選項來組織代碼:

        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具體的用法:

        舉個栗子


        很多童鞋可能就有疑惑了,這跟我在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

        所謂的Fragment,就是片段;在vue2.x中,要求每個模板必須有一個根節(jié)點,所以我們代碼要這樣寫:

        <template>
        ??<div>
        ????<span>span>
        ????<span>span>
        ??div>
        template>

        或者在Vue2.x中還可以引入vue-fragments庫,用一個虛擬的fragment代替div;在React中,解決方法是通過的一個React.Fragment標(biāo)簽創(chuàng)建一個虛擬元素;在Vue3中我們可以直接不需要根節(jié)點:

        <template>
        ????<span>hellospan>
        ????<span>worldspan>
        template>

        這樣就少了很多沒有意義的div元素。

        Teleport

        Teleport翻譯過來就是傳送、遠(yuǎn)距離傳送的意思;顧名思義,它可以將插槽中的元素或者組件傳送到頁面的其他位置:

        傳送門游戲

        在React中可以通過createPortal函數(shù)來創(chuàng)建需要傳送的節(jié)點;本來尤大大想起名叫Portal,但是H5原生的Portal標(biāo)簽也在計劃中,雖然有一些安全問題,但是為了避免重名,因此改成Teleport。

        Teleport一個常見的使用場景,就是在一些嵌套比較深的組件來轉(zhuǎn)移模態(tài)框的位置。雖然在邏輯上模態(tài)框是屬于該組件的,但是在樣式和DOM結(jié)構(gòu)上,嵌套層級后較深后不利于進(jìn)行維護(hù)(z-index等問題);因此我們需要將其進(jìn)行剝離出來:



        這里的Teleport中的modal div就被傳送到了body的底部;雖然在不同的地方進(jìn)行渲染,但是Teleport中的元素和組件還是屬于父組件的邏輯子組件,還是可以和父組件進(jìn)行數(shù)據(jù)通信。Teleport接收兩個參數(shù)todisabled

        • to - string:必須是有效的查詢選擇器或 HTMLElement,可以id或者class選擇器等。
        • disabled - boolean:如果是true表示禁用teleport的功能,其插槽內(nèi)容將不會移動到任何位置,默認(rèn)false不禁用。

        Suspense

        Suspense是Vue3推出的一個內(nèi)置組件,它允許我們的程序在等待異步組件時渲染一些后備的內(nèi)容,可以讓我們創(chuàng)建一個平滑的用戶體驗;Vue中加載異步組件其實在Vue2.x中已經(jīng)有了,我們用的vue-router中加載的路由組件其實也是一個異步組件:

        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):



        異步組件加載顯示占位

        非兼容的功能

        非兼容的功能主要是一些和Vue2.x版本改動較大的語法,已經(jīng)在Vue3上可能存在兼容問題了。

        data、mixin和filter

        在Vue2.x中,我們可以定義data為object或者function,但是我們知道在組件中如果data是object的話會出現(xiàn)數(shù)據(jù)互相影響,因為object是引用數(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)容的展示:



        最常見的就是處理一些訂單的文案展示等;然而在vue3中,過濾器filter已經(jīng)刪除,不再支持了,官方建議使用方法調(diào)用或者計算屬性computed來進(jìn)行代替。

        v-model

        在Vue2.x中,v-model相當(dāng)于綁定value屬性和input事件,它本質(zhì)也是一個語法糖:

        <child-component?v-model="msg">child-component>

        <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>



        <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>


        <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>


        <child-component?
        ????:msg1="msg1"?
        ????@update:msg1="msg1=$event"
        ????:msg2="msg2"
        ????@update:msg2="msg2=$event">

        child-component>

        v-for和key

        在Vue2.x中,我們都知道v-for每次循環(huán)都需要給每個子節(jié)點一個唯一的key,還不能綁定在template標(biāo)簽上,

        <template?v-for="item?in?list">
        ??<div?:key="item.id">...div>
        ??<span?:key="item.id">...span>
        template>

        而在Vue3中,key值應(yīng)該被放置在template標(biāo)簽上,這樣我們就不用為每個子節(jié)點設(shè)一遍:

        <template?v-for="item?in?list"?:key="item.id">
        ??<div>...div>
        ??<span>...span>
        template>

        v-bind合并

        在vue2.x中,如果一個元素同時定義了v-bind="object"和一個相同的單獨(dú)的屬性,那么這個單獨(dú)的屬性會覆蓋object中的綁定:

        <div?id="red"?v-bind="{?id:?'blue'?}">div>
        <div?v-bind="{?id:?'blue'?}"?id="red">div>


        <div?id="red">div>

        然而在vue3中,如果一個元素同時定義了v-bind="object"和一個相同的單獨(dú)的屬性,那么聲明綁定的順序決定了最后的結(jié)果(后者覆蓋前者):


        <div?id="red"?v-bind="{?id:?'blue'?}">div>

        <div?id="blue">div>


        <div?v-bind="{?id:?'blue'?}"?id="red">div>

        <div?id="red">div>

        v-for中ref

        vue2.x中,在v-for上使用ref屬性,通過this.$refs會得到一個數(shù)組:

          



        但是這樣可能不是我們想要的結(jié)果;因此vue3不再自動創(chuàng)建數(shù)組,而是將ref的處理方式變?yōu)榱撕瘮?shù),該函數(shù)默認(rèn)傳入該節(jié)點:

          



        v-for和v-if優(yōu)先級

        在vue2.x中,在一個元素上同時使用v-for和v-if,v-for有更高的優(yōu)先級,因此在vue2.x中做性能優(yōu)化,有一個重要的點就是v-for和v-if不能放在同一個元素上。

        而在vue3中,v-ifv-for有更高的優(yōu)先級。因此下面的代碼,在vue2.x中能正常運(yùn)行,但是在vue3中v-if生效時并沒有item變量,因此會報錯:




        總結(jié)

        以上就是Vue3.0作為終端用的我們可能會涉及到的一些新特性和新功能,其實Vue3.0還有很多的改動,這里由于篇幅原因就不一一展開了,大家可以自行查閱官方文檔,期待Vue3能帶給我們更便利更友好的開發(fā)體驗。

        1. JavaScript 重溫系列(22篇全)
        2. ECMAScript 重溫系列(10篇全)
        3. JavaScript設(shè)計模式 重溫系列(9篇全)
        4.?正則 / 框架 / 算法等 重溫系列(16篇全)
        5.?Webpack4 入門(上)||?Webpack4 入門(下)
        6.?MobX 入門(上)?||??MobX 入門(下)
        7. 120+篇原創(chuàng)系列匯總

        回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

        點擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

        瀏覽 83
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. jZZijZZij亚洲日本少妇 | 娇妻淫伦3亲p视频 | 荡公乱妇11部分 | 黄色视频网站免费观看 | 2021国产在线视频 |