<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 10477字,需瀏覽 21分鐘

           ·

          2021-01-25 13:34

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



          前言


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




          案例??


          在正式介紹之前,大家可以先跑一下這個 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(()?=>?{
          ??????//?在掛載后請求數(shù)據(jù)
          ??????getList();
          ????})
          ??}
          };




          vue3常用api


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


          setup()


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


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


          • props

          • context


          其中context是一個上下文對象,具有屬性(attrs,slots,emit,parent,root),其對應(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中的變量時,直接訪問變量名就可以使用。


          擴(kuò)展


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


          1. 組件使用props的場景更多,有時甚至只需要使用props

          2. 將props獨立出來作為一個參數(shù),可以讓TypeScript對props單獨做類型推導(dǎo),不會和上下文中其他屬性混淆。這也使得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ù)組和對象新增屬性的痛點


          用法






          效果如下??




          區(qū)別


          • 使用時在setup函數(shù)中需要通過內(nèi)部屬性.value來訪問ref數(shù)據(jù),return出去的ref可直接訪問,因為在返回時已經(jīng)自動解套;reactive可以直接通過創(chuàng)建對象訪問

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


          toRefs


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


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


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






          toRef


          toRef?用來將引用數(shù)據(jù)類型或者reavtive數(shù)據(jù)類型中的某個值轉(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?是對原數(shù)據(jù)的拷貝,響應(yīng)式數(shù)據(jù)對象值改變后會同步更新視圖,不會影響到原始值。

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


          isRef


          判斷是否是ref對象,內(nèi)部是判斷數(shù)據(jù)對象上是否包含__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的語法糖。


          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ū)別


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


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

          • callback:依賴對象變化后執(zhí)行的回調(diào)函數(shù),帶有2個參數(shù):newVal,oldVal。如果要監(jiān)聽多個數(shù)據(jù)每個參數(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)聽多個ref
          ??????watch([title,?num],?([newTitle,?newNum],?[oldTitle,?oldNum])?=>?{
          ????????/*?...?*/
          ??????});??????
          ??????
          ??????//?監(jiān)聽reactive多個值
          ??????watch([()?=>?state.list,?()?=>?state.nums],?([newList,?newNums],?[oldList,?oldvNums])?=>?{
          ????????/*?...?*/
          ??????});


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







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


          computed


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


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


          //?父組件


          //?孫組件


          getCurrentInstance


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


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

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


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


          $Refs


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






          .sync


          在vue2.0中使用.sync實現(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單一指令語法。速記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實例,useStore可以獲取實例,作用等同于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)到首頁
          ??????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?兩個新增的 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 的對象包含下面幾個屬性:


          1. href:解析后的 URL。將會作為一個 a 元素的 href attribute。

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

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

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

          5. isExactActive:如果需要應(yīng)用精確激活的 class 則為 true。允許應(yīng)用一個任意的 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的時候當(dāng)我們一個組件混入大量不同的mixin的時候,會存在兩個非常明顯的問題:命名沖突和數(shù)據(jù)來源不清晰。


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

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


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


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


          用法如下:






          假設(shè)這邊我們引用了counter和getTime兩個mixin,則無法確認(rèn)count和increment()方法來源,并且兩個mixin中可能會出現(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ù),也不會出現(xiàn)命名沖突的問題。??


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




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

          -?END -

          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧洲一区二区三区 | 欧美老熟妇乱子伦视频 | 夜色视频在线免费观看 | 波多野结衣AV一区二区 | 综合插插插网 |