1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Vue3 實(shí)戰(zhàn)筆記 | 快速入門

        共 10477字,需瀏覽 21分鐘

         ·

        2021-01-25 13:34

        作者:三余
        來源:SegmentFault 思否社區(qū)



        前言


        vue3正式版已經(jīng)發(fā)布好幾個(gè)月了。相信有不少人早已躍躍欲試,這里根據(jù)這幾天的項(xiàng)目經(jīng)驗(yàn)羅列幾點(diǎn)在項(xiàng)目中可能用到的知識(shí)點(diǎn)跟大家分享總結(jié),在展開功能點(diǎn)介紹之前,先從一個(gè)簡(jiǎn)單的demo幫助大家可以快速入手新項(xiàng)目??




        案例??


        在正式介紹之前,大家可以先跑一下這個(gè) demo 快速熟悉用法






        效果如下??





        vue3生命周期


        vue3的生命周期函數(shù)只能用在setup()里使用,變化如下??


        vue2vue3
        beforeCreatesetup
        createdsetup
        beforeMountonBeforeMount
        mountedonMounted
        beforeUpdateonBeforeUpdate
        updatedonUpdated
        beforeDestroyonBeforeUnmount
        destroyedonUnmounted
        activatedonActivated
        deactivatedonDeactivated


        擴(kuò)展


        1. 可以看出來vue2的beforeCreate和created變成了setup

        2. 絕大部分生命周期都是在原本vue2的生命周期上帶上了on前綴


        使用


        在setup中使用生命周期:


        import?{??onMounted?}?from?'vue';

        export?default?{
        ??setup()?{
        ????onMounted(()?=>?{
        ??????//?在掛載后請(qǐng)求數(shù)據(jù)
        ??????getList();
        ????})
        ??}
        };




        vue3常用api


        上述案例中使用了一些常用的api,下面帶大家一一認(rèn)識(shí)下我們的新朋友


        setup()


        setup函數(shù)是一個(gè)新的組件選項(xiàng)。作為在組件內(nèi)使用Composition API的入口點(diǎn)。從生命周期鉤子的視角來看,它會(huì)在beforeCreate鉤子之前被調(diào)用,所有變量、方法都在setup函數(shù)中定義,之后return出去供外部使用


        該函數(shù)有2個(gè)參數(shù):


        • props

        • context


        其中context是一個(gè)上下文對(duì)象,具有屬性(attrs,slots,emit,parent,root),其對(duì)應(yīng)于vue2中的this.$attrs,this.$slots,this.$emit,this.$parent,this.$root。


        setup也用作在tsx中返回渲染函數(shù):

        import?{??onMounted?}?from?'vue';
        setup(props,?{?attrs,?slots?})?{
        ????return?()?=>?{
        ??????const?propsData?=?{?...attrs,?...props?}?as?any;
        ??????return?{extendSlots(slots)};
        ????};
        ??},
        *注意:this關(guān)鍵字在setup()函數(shù)內(nèi)部不可用,在方法中訪問setup中的變量時(shí),直接訪問變量名就可以使用。


        擴(kuò)展


        為什么props沒有被包含在上下文中?


        1. 組件使用props的場(chǎng)景更多,有時(shí)甚至只需要使用props

        2. 將props獨(dú)立出來作為一個(gè)參數(shù),可以讓TypeScript對(duì)props單獨(dú)做類型推導(dǎo),不會(huì)和上下文中其他屬性混淆。這也使得setup、render和其他使用了TSX的函數(shù)式組件的簽名保持一致。


        reactive, ref


        reactive和ref都是vue3中用來創(chuàng)建響應(yīng)式數(shù)據(jù)的api,作用等同于在vue2中的data,不同的是他們使用了ES6的Porxy API解決了vue2?defineProperty?無法監(jiān)聽數(shù)組和對(duì)象新增屬性的痛點(diǎn)


        用法






        效果如下??




        區(qū)別


        • 使用時(shí)在setup函數(shù)中需要通過內(nèi)部屬性.value來訪問ref數(shù)據(jù),return出去的ref可直接訪問,因?yàn)樵诜祷貢r(shí)已經(jīng)自動(dòng)解套;reactive可以直接通過創(chuàng)建對(duì)象訪問

        • ref接受一個(gè)參數(shù),返回響應(yīng)式ref對(duì)象,一般是基本類型值(String?、Nmuber?、Boolean?等)或單值對(duì)象。如果傳入的參數(shù)是一個(gè)對(duì)象,將會(huì)調(diào)用?reactive?方法進(jìn)行深層響應(yīng)轉(zhuǎn)換(此時(shí)訪問ref中的對(duì)象會(huì)返回Proxy對(duì)象,說明是通過reactive創(chuàng)建的);引用類型值(Object?、Array)使用reactive


        toRefs


        將傳入的對(duì)象里所有的屬性的值都轉(zhuǎn)化為響應(yīng)式數(shù)據(jù)對(duì)象(ref)


        使用reactive?return 出去的值每個(gè)都需要通過reactive對(duì)象 .屬性的方式訪問非常麻煩,我們可以通過解構(gòu)賦值的方式范圍,但是直接解構(gòu)的參數(shù)不具備響應(yīng)式,此時(shí)可以使用到這個(gè)api(也可以對(duì)props中的響應(yīng)式數(shù)據(jù)做此處理)


        將前面的例子作如下??修改使用起來更加方便:






        toRef


        toRef?用來將引用數(shù)據(jù)類型或者reavtive數(shù)據(jù)類型中的某個(gè)值轉(zhuǎn)化為響應(yīng)式數(shù)據(jù)


        用法

        • reactive數(shù)據(jù)類型


        /*?reactive數(shù)據(jù)類型?*/
        ??????let?obj?=?reactive({?name:?'小黃',?sex:?'1'?});
        ??????let?state?=?toRef(obj,?'name');

        ??????state.value?=?'小紅';
        ??????console.log(obj.name);?//?小紅
        ??????console.log(state.value);?//?小紅

        ??????obj.name?=?'小黑';
        ??????console.log(obj.name);?//?小黑
        ??????console.log(state.value);?//?小黑


        • 引用數(shù)據(jù)類型







        小結(jié)


        • ref?是對(duì)原數(shù)據(jù)的拷貝,響應(yīng)式數(shù)據(jù)對(duì)象值改變后會(huì)同步更新視圖,不會(huì)影響到原始值。

        • toRef?是對(duì)原數(shù)據(jù)的引用,響應(yīng)式數(shù)據(jù)對(duì)象值改變后不會(huì)改變視圖,會(huì)影響到原始值。


        isRef


        判斷是否是ref對(duì)象,內(nèi)部是判斷數(shù)據(jù)對(duì)象上是否包含__v_isRef屬性且值為true。


        setup()?{
        ??????const?one?=?ref(0);
        ??????const?two?=?0;
        ??????const?third?=?reactive({
        ????????data:?'',
        ??????});
        ??????let?four?=?toRef(third,?'data');
        ??????const?{?data?}?=?toRefs(third);
        ??????
        ??????console.log(isRef(one));?//?true
        ??????console.log(isRef(data));?//?true
        ??????console.log(isRef(four));?//?true
        ??????console.log(isRef(two));?//?false
        ??????console.log(isRef(third));?//?false
        ????}


        unref


        如果參數(shù)為ref,則返回內(nèi)部原始值,否則返回參數(shù)本身。內(nèi)部是val = isRef(val) ? val.value : val的語(yǔ)法糖。


        setup()?{
        ??????const?hello?=?ref('hello');
        ??????console.log(hello);?//?{?__v_isRef:?true,value:?"hello"...?}
        ??????const?newHello?=?unref(hello);
        ??????console.log(newHello);?//?hello
        ????}


        watch, watchEffect


        watch


        watch偵聽器,監(jiān)聽數(shù)據(jù)變化


        用法和vue2有些區(qū)別


        語(yǔ)法為:watch(source, callback, options)


        • source:用于指定監(jiān)聽的依賴對(duì)象,可以是表達(dá)式,getter函數(shù)或者包含上述兩種類型的數(shù)組(如果要監(jiān)聽多個(gè)值)

        • callback:依賴對(duì)象變化后執(zhí)行的回調(diào)函數(shù),帶有2個(gè)參數(shù):newVal,oldVal。如果要監(jiān)聽多個(gè)數(shù)據(jù)每個(gè)參數(shù)可以是數(shù)組?[newVal1, newVal2, ... newValN],[oldVal1, oldVal2, ... oldValN]

        • options:可選參數(shù),用于配置watch的類型,可以配置的屬性有?immediate(立即觸發(fā)回調(diào)函數(shù))、deep(深度監(jiān)聽)


        let?title?=?ref('Create');
        ??????let?num?=?ref(0);
        ??????const?state?=?reactive({
        ????????nums:?0,
        ????????list:?[],
        ??????});
        ??????
        ??????//?監(jiān)聽ref
        ??????watch(title,?(newValue,?oldValue)?=>?{
        ?????????/*?...?*/
        ??????});

        ??????//?監(jiān)聽reactive
        ??????watch(
        ????????//?getter
        ????????()?=>?state.list.length,
        ????????//?callback
        ????????(v?=?0)?=>?{
        ??????????state.nums?=?v;
        ????????},
        ?????????//?watch?Options
        ????????{?immediate:?true?}
        ??????);
        ??????
        ??????//?監(jiān)聽多個(gè)ref
        ??????watch([title,?num],?([newTitle,?newNum],?[oldTitle,?oldNum])?=>?{
        ????????/*?...?*/
        ??????});??????
        ??????
        ??????//?監(jiān)聽reactive多個(gè)值
        ??????watch([()?=>?state.list,?()?=>?state.nums],?([newList,?newNums],?[oldList,?oldvNums])?=>?{
        ????????/*?...?*/
        ??????});


        我們可以向上面一樣將多個(gè)值的監(jiān)聽拆成多個(gè)對(duì)單個(gè)值監(jiān)聽的watch。這有助于我們組織代碼并創(chuàng)建具有不同選項(xiàng)的觀察者;watch方法會(huì)返回一個(gè)stop()方法,若想要停止監(jiān)聽,便可直接執(zhí)行該stop函數(shù)
        watchEffect
        立即執(zhí)行傳入的一個(gè)函數(shù),并響應(yīng)式追蹤其依賴,并在其依賴變更是重新運(yùn)行該函數(shù).







        可以看到在組件初始化的時(shí)候該回調(diào)函數(shù)立即執(zhí)行了一次,同時(shí)開始自動(dòng)檢測(cè)回調(diào)函數(shù)里頭依賴的值,并在依賴關(guān)系發(fā)生改變時(shí)自動(dòng)觸發(fā)這個(gè)回調(diào)函數(shù),這樣我們就不必手動(dòng)傳入依賴特意去監(jiān)聽某個(gè)值了


        computed


        傳入一個(gè)getter函數(shù),返回一個(gè)默認(rèn)不可手動(dòng)修改的ref對(duì)象.


        setup()?{
        ??????let?title?=?ref('Create');
        ??????const?vTitle?=?computed(()?=>?'-'?+?title.value?+?'-');
        ??????
        ??????function?handleClick()?{
        ????????if?(title.value?===?'Create')?{
        ??????????title.value?=?'Reset';
        ????????}?else?{
        ??????????title.value?=?'Create';
        ????????}
        ??????}
        ??????}


        反轉(zhuǎn)字符串:


        setup()?{
        ????const?state?=?reactive({
        ??????value:?'',
        ??????rvalue:?computed(()?=>
        ????????state.value
        ??????????.split('')
        ??????????.reverse()
        ??????????.join('')
        ??????)
        ????})
        ????return?toRefs(state)
        ??}


        provide, inject


        provide()和inject()用來實(shí)現(xiàn)多級(jí)嵌套組件之間的數(shù)據(jù)傳遞,父組件或祖先組件使用?provide()向下傳遞數(shù)據(jù),子組件或子孫組件使用inject()來接收數(shù)據(jù)


        //?父組件


        //?孫組件


        getCurrentInstance


        getCurrentInstance方法用于獲取當(dāng)前組件實(shí)例,僅在setup和生命周期中起作用


        import?{?getCurrentInstance,?onBeforeUnmount?}?from?'vue';

        const?instance?=?getCurrentInstance();
        //?判斷當(dāng)前組件實(shí)例是否存在
        if?(instance)?{
        ????onBeforeUnmount(()?=>?{
        ????????/*?...?*/
        ?????});
        ?}


        通過instance中的ctx屬性可以獲得當(dāng)前上下文,通過這個(gè)屬性可以使用組件實(shí)例中的各種全局變量和屬性


        $Refs


        為了獲得對(duì)模板中元素或組件實(shí)例的引用,我們可以同樣使用ref并從setup()返回它






        .sync


        在vue2.0中使用.sync實(shí)現(xiàn)prop的雙向數(shù)據(jù)綁定,在vue3中將它合并到了v-model里


        vue2.0


        ??????????:current-page.sync="currentPage1"
        ????>
        ????


        vue3.0


        ??????????v-model:current-page="currentPage1"
        ????>
        ????


        v-slot


        Child.vue






        vue2用法




        vue3用法


        新指令v-slot統(tǒng)一slot和slot-scope單一指令語(yǔ)法。速記v-slot可以潛在地統(tǒng)一作用域和普通插槽的用法。



        ??????"user">
        ????????

          ??????????"(item,?index)?in?user.data"?:key="index">{{?item?}}
          ????????

        ??????
        ??????"user">
        ????????
        {{?user.data?}}

        ??????
        ??????
        ??????#two="user">
        ??????
        {{?user.data?}}

        ??????
        ????
        ??






        Composition API 結(jié)合vuex4, Vue Router 4


        createStore,useStore,useRouter,useRoute


        在vuex4中通過createStore創(chuàng)建Vuex實(shí)例,useStore可以獲取實(shí)例,作用等同于vue2.0中的this.$store;

        Vue Router 4?中useRouter可以獲取路由器,用來進(jìn)行路由的跳轉(zhuǎn),作用等同于vue2.0的this.$router,useRoute就是鉤子函數(shù)相當(dāng)于vue2.0的this.$route


        store/index.ts


        import?{createStore}?from?'vuex';
        const?store?=?createStore({
        ??state:?{
        ????user:?null,
        ??},
        ??mutations:?{
        ????setUser(state,?user)?{
        ??????state.user?=?user;
        ????}
        ??},
        ??actions:?{},
        ??modules:?{}
        });


        router/index.ts


        import?{?createRouter,?createWebHashHistory,?RouteRecordRaw?}?from?'vue-router';
        import?{?scrollBehavior?}?from?'./scrollBehaviour.ts';

        const?routes:?Array?=?[
        ??{
        ????path:?'/',
        ????name:?'Home',
        ????component:?()?=>?import('/@/views/home.vue')?//?vite.config.vue中配置alias
        ??}
        ];

        const?router?=?createRouter({
        ??history:?createWebHashHistory(),
        ??routes,
        ??strict:?true,
        ??scrollBehavior:?scrollBehavior,
        });

        export?default?router;


        main.ts


        import?{?createApp?}?from?'vue';
        import?App?from?'./App.vue';
        import?router?from?'./router';
        import?store?from?'./store';
        import?{?getTime?}?from?'/@/utils'

        const?app?=?createApp(App);
        app.config.globalProperties.$getTime?=?getTime?//?vue3配置全局變量,取代vue2的Vue.prototype
        app.use(store).use(router)
        app.mount('#app');


        App.vue


        import?{?reactive?}?from?"vue";
        import?{?useRouter?}?from?"vue-router";
        import?{?useStore?}?from?"vuex";
        import?{?ElMessage?}?from?'element-plus';
        export?default?{
        ??name:?"App",
        ??setup()?{
        ????const?store?=?useStore();
        ????const?router?=?useRouter();
        ????//?用戶名和密碼
        ????const?Form?=?reactive({
        ??????username:?"johnYu",
        ??????password:?"123456",
        ????});
        ????//?登錄
        ????function?handelLogin()?{
        ??????store.commit("setUser",?{
        ????????username:?Form.username,
        ????????password:?Form.password,
        ??????});
        ??????ElMessage({
        ????????type:?'success',
        ????????message:?'登陸成功',
        ????????duration:?1500,
        ??????});
        ??????//?跳轉(zhuǎn)到首頁(yè)
        ??????router.push({
        ?????????name:?'Home',
        ?????????params:?{
        ???????????username:?Form.username
        ?????????},
        ??????});
        ????}
        ????return?{
        ??????Form,
        ??????handelLogin
        ??????};
        ??}


        home.vue


        import?{?useRouter,?useRoute?}?from?'vue-router';
        ??import?Breadcrumb?from?'/@/components/Breadcrumb.vue';

        ??export?default?defineComponent({
        ????name:?'Home',
        ????components:?{
        ??????Breadcrumb,
        ????},
        ????setup()?{
        ??????const?route?=?useRoute();
        ??????//?接收參數(shù)
        ??????const?username?=?route.params.username;
        ??????return?{username}
        ????}
        ????})


        導(dǎo)航守衛(wèi)


        由于使用 Composition API 的原因,setup函數(shù)里面分別使用onBeforeRouteLeave和onBeforeRouteUpdate?兩個(gè)新增的 API 代替vue2.0中的beforeRouteLeave和beforeRouteUpdate?。


        import?{?onBeforeRouteUpdate,?onBeforeRouteLeave?}?from?'vue-router';
        ???setup()?{
        ??????onBeforeRouteUpdate((to)?=>?{
        ????????if?(to.name?===?'Home'){
        ????????????/*?...?*/
        ????????}
        ??????});
        ???}


        useLink


        useLink它提供與router-link的v-slot?API 相同的訪問權(quán)限,將RouterLink的內(nèi)部行為公開為Composition API函數(shù),用于暴露底層的定制能力






        插槽 prop 的對(duì)象包含下面幾個(gè)屬性:


        1. href:解析后的 URL。將會(huì)作為一個(gè) a 元素的 href attribute。

        2. route:解析后的規(guī)范化的地址。

        3. navigate:觸發(fā)導(dǎo)航的函數(shù)。會(huì)在必要時(shí)自動(dòng)阻止事件,和 router-link 同理。

        4. isActive:如果需要應(yīng)用激活的 class 則為 true。允許應(yīng)用一個(gè)任意的 class。

        5. isExactActive:如果需要應(yīng)用精確激活的 class 則為 true。允許應(yīng)用一個(gè)任意的 class。




        擴(kuò)展


        樣式 scoped


        vue2


        /*?深度選擇器?*/
        /*方式一:*/
        >>>?.foo{?}
        /*方式二:*/
        /deep/?.foo{?}
        /*方式三*/
        ::v-deep?.foo{?}


        vue3


        /*?深度選擇器?*/
        ::v-deep(.foo)?{}


        .env環(huán)境擴(kuò)展


        vite中的.env文件變量名一定要以VITE_前綴


        .env文件


        VITE_USE_MOCK?=?true


        使用:


        import.meta.env.VITE_APP_CONTEXT




        使用Composition API替換mixin


        眾所周知使用mixin的時(shí)候當(dāng)我們一個(gè)組件混入大量不同的mixin的時(shí)候,會(huì)存在兩個(gè)非常明顯的問題:命名沖突和數(shù)據(jù)來源不清晰。


        • 每個(gè)mixin都可以定義自己的props、data,它們之間是無感的,所以很容易定義相同的變量,導(dǎo)致命名沖突。

        • 另外對(duì)組件而言,如果模板中使用不在當(dāng)前組件中定義的變量,那么就會(huì)不太容易知道這些變量在哪里定義的,這就是數(shù)據(jù)來源不清晰。


        以這個(gè)經(jīng)典的Vue 2組件為例,它定義了一個(gè)"計(jì)數(shù)器"功能:


        //counter.js
        export?default?{
        ??data()?{
        ????return?{
        ??????count:?0
        ????};
        ??},
        ??methods:?{
        ????increment()?{
        ??????this.count++;
        ????}
        ??}
        }


        用法如下:






        假設(shè)這邊我們引用了counter和getTime兩個(gè)mixin,則無法確認(rèn)count和increment()方法來源,并且兩個(gè)mixin中可能會(huì)出現(xiàn)重復(fù)命名的概率


        下面是使用Composition API定義的完全相同的組件:


        //?counter.ts
        import?{?ref?}?from?'vue';

        export?default?function?()?{
        ????const?count?=?ref(0);
        ????function?increment()?{
        ????????count.value++;
        ????}
        ????return?{?count,?increment?};
        }







        總結(jié)


        使用Composition API可以清晰的看到數(shù)據(jù)來源,即使去編寫更多的hook函數(shù),也不會(huì)出現(xiàn)命名沖突的問題。??


        Composition API 除了在邏輯復(fù)用方面有優(yōu)勢(shì),也會(huì)有更好的類型支持,因?yàn)樗鼈兌际且恍┖瘮?shù),在調(diào)用函數(shù)時(shí),自然所有的類型就被推導(dǎo)出來了,不像 Options API 所有的東西使用 this。另外,Composition API 對(duì) tree-shaking 友好,代碼也更容易壓縮。vue3的Composition API會(huì)將某個(gè)邏輯關(guān)注點(diǎn)相關(guān)的代碼全都放在一個(gè)函數(shù)里,這樣當(dāng)需要修改一個(gè)功能時(shí),就不再需要在文件中跳來跳去




        點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動(dòng)和交流。

        -?END -

        瀏覽 30
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            变态撕了美女裙子摸下身 | 黄色A大片 | 色8视频| 骚逼站 | 黄色三级片视频网站 | 青娱乐一级无码 | 欧美成人A片无码高清 | 天干天干夜夜爽 | 欧美性综合网 | 国产精品秘 国产A级 |