溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue3中怎么利用CompositionAPI優(yōu)化代碼量

發(fā)布時(shí)間:2021-07-09 11:12:53 來源:億速云 閱讀:153 作者:Leah 欄目:web開發(fā)

Vue3中怎么利用CompositionAPI優(yōu)化代碼量,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

我們先來看看組件的整體代碼結(jié)構(gòu),如下圖所示:

Vue3中怎么利用CompositionAPI優(yōu)化代碼量

image-20210114095802363

  • template部分占用267行

  • script部分占用889行

  • style部分為外部引用占用1行

罪魁禍?zhǔn)拙褪莝cript部分,本文要優(yōu)化的就是這一部分的代碼,我們再來細(xì)看下script中的代碼結(jié)構(gòu):

  • props部分占用6行

  • data部分占用52行

  • created部分占用8行

  • mounted部分占用98行

  • methods部分占用672行

  • emits部分占用6行

  • computed部分占用8行

  • watch部分占用26行

現(xiàn)在罪魁禍?zhǔn)资莔ethods部分,那么我們只需要把methods部分的代碼拆分出去,單文件代碼量就大大減少了。

優(yōu)化方案

經(jīng)過上述分析后,我們已經(jīng)知道了問題所在,接下來就跟大家分享下我一開始想到的方案以及最終所采用的方案。

直接拆分成文件

一開始我覺得既然methods方法占用的行數(shù)太多,那么我在src下創(chuàng)建一個(gè)methods文件夾,把每個(gè)組件中的methods的方法按照組件名進(jìn)行劃分,創(chuàng)建對應(yīng)的文件夾,在對應(yīng)的組件文件夾內(nèi)部,將methods中的方法拆分成獨(dú)立的ts文件,最后創(chuàng)建index.ts文件,將其進(jìn)行統(tǒng)一導(dǎo)出,在組件中使用時(shí)按需導(dǎo)入index.ts中暴露出來的模塊,如下圖所示:

Vue3中怎么利用CompositionAPI優(yōu)化代碼量

image-20210114103824562

  • 創(chuàng)建methods文件夾

  • 把每個(gè)組件中的methods的方法按照組件名進(jìn)行劃分,創(chuàng)建對應(yīng)的文件夾,即:message-display

  • 將methods中的方法拆分成獨(dú)立的ts文件,即:message-display文件夾下的ts文件

  • 創(chuàng)建index.ts文件,即:methods下的index.ts文件

index.ts代碼

如下所示,我們將拆分的模塊方法進(jìn)行導(dǎo)入,然后統(tǒng)一export出去

import compressPic from "@/methods/message-display/CompressPic"; import pasteHandle from "@/methods/message-display/PasteHandle";  export { compressPic, pasteHandle };

在組件中使用

最后,我們在組件中按需導(dǎo)入即可,如下所示:

import { compressPic, pasteHandle } from "@/methods/index";  export default defineComponent({     mounted() {       compressPic();       pasteHandle();     } })

運(yùn)行結(jié)果

當(dāng)我自信滿滿的開始跑項(xiàng)目時(shí),發(fā)現(xiàn)瀏覽器的控制臺(tái)報(bào)錯(cuò)了,提示我this未定義,突然間我意識(shí)到將代碼拆分成文件后,this是指向那個(gè)文件的,并沒有指向當(dāng)前組件實(shí)例,當(dāng)然可以將this作為參數(shù)傳進(jìn)去,但我覺得這樣并不妥,用到一個(gè)方法就傳一個(gè)this進(jìn)去,會(huì)產(chǎn)生很多冗余代碼,因此這個(gè)方案被我pass了。

使用mixins

前一個(gè)方案因?yàn)閠his的問題以失敗告終,在Vue2.x的時(shí)候官方提供了mixins來解決this問題,我們使用mixin來定義我們的函數(shù),最后使用mixins進(jìn)行混入,這樣就可以在任意地方使用了。

由于mixins是全局混入的,一旦有重名的mixin原來的就會(huì)被覆蓋,所以這個(gè)方案也不合適,pass。

Vue3中怎么利用CompositionAPI優(yōu)化代碼量

image-20210114111746208

使用CompositionAPI

上述兩個(gè)方案都不合適,那 么CompositionAPI就剛好彌補(bǔ)上述方案的短處,成功的實(shí)現(xiàn)了我們想要實(shí)現(xiàn)的需求。

我們先來看看什么是CompositionAPI,正如文檔所述,我們可以將原先optionsAPI中定義的函數(shù)以及這個(gè)函數(shù)需要用到的data變量,全部歸類到一起,放到setup函數(shù)里,功能開發(fā)完成后,將組件需要的函數(shù)和data在setup進(jìn)行return。

setup函數(shù)在創(chuàng)建組件之前執(zhí)行,因此它是沒有this的,這個(gè)函數(shù)可以接收2個(gè)參數(shù): props和context,他們的類型定義如下:

interface Data {   [key: string]: unknown }  interface SetupContext {   attrs: Data   slots: Slots   emit: (event: string, ...args: unknown[]) => void } function setup(props: Data, context: SetupContext): Data

我的組件需要拿到父組件傳過來的props中的值,需要通過emit來向父組件傳遞數(shù)據(jù),props和context這兩個(gè)參數(shù)正好解決了我這個(gè)問題。

setup又是個(gè)函數(shù),也就意味著我們可以將所有的函數(shù)拆分成獨(dú)立的ts文件,然后在組件中導(dǎo)入,在setup中將其return給組件即可,這樣就很完美的實(shí)現(xiàn)了一開始我們一開始所說的的拆分。

實(shí)現(xiàn)思路

接下來的內(nèi)容會(huì)涉及到響應(yīng)性API,如果對響應(yīng)式API不了解的開發(fā)者請先移步官方文檔。

我們分析出方案后,接下來我們就來看看具體的實(shí)現(xiàn)路:

  • 在組件的導(dǎo)出對象中添加setup屬性,傳入props和context

  • 在src下創(chuàng)建module文件夾,將拆分出來的功能代碼按組件進(jìn)行劃分

  • 將每一個(gè)組件中的函數(shù)進(jìn)一步按功能進(jìn)行細(xì)分,此處我分了四個(gè)文件夾出來

    • common-methods 公共方法,存放不需要依賴組件實(shí)例的方法

    • components-methods 組件方法,存放當(dāng)前組件模版需要使用的方法

    • main-entrance 主入口,存放setup中使用的函數(shù)

    • split-method 拆分出來的方法,存放需要依賴組件實(shí)例的方法,setup中函數(shù)拆分出來的文件也放在此處

  • 在主入口文件夾中創(chuàng)建InitData.ts文件,該文件用于保存、共享當(dāng)前組件需要用到的響應(yīng)式data變量

  • 所有函數(shù)拆分完成后,我們在組件中將其導(dǎo)入,在setup中進(jìn)行return即可

實(shí)現(xiàn)過程

接下來我們將上述思路進(jìn)行實(shí)現(xiàn)。

添加setup選項(xiàng)

我們在vue組件的導(dǎo)出部分,在其對象內(nèi)部添加setup選項(xiàng),如下所示:

<template>   <!---其他內(nèi)容省略--> </template> <script lang="ts"> export default defineComponent({   name: "message-display",   props: {     listId: String, // 消息id     messageStatus: Number, // 消息類型     buddyId: String, // 好友id     buddyName: String, // 好友昵稱     serverTime: String // 服務(wù)器時(shí)間   },   setup(props, context) {     // 在此處即可寫響應(yīng)性API提供的方法,注意??此處不能用this   } } </script>

創(chuàng)建module模塊

我們在src下創(chuàng)建module文件夾,用于存放我們拆分出來的功能代碼文件。

如下所示,為我創(chuàng)建好的目錄,我的劃分依據(jù)是將相同類別的文件放到一起,每個(gè)文件夾的所代表的含義已在實(shí)現(xiàn)思路進(jìn)行說明,此處不作過多解釋。

Vue3中怎么利用CompositionAPI優(yōu)化代碼量

創(chuàng)建InitData.ts文件

我們將組件中用到的響應(yīng)式數(shù)據(jù),統(tǒng)一在這里進(jìn)行定義,然后在setup中進(jìn)行return,該文件的部分代碼定義如下,完整代碼請移步:InitData.ts

import {   reactive,   Ref,   ref,   getCurrentInstance,   ComponentInternalInstance } from "vue"; import {   emojiObj,   messageDisplayDataType,   msgListType,   toolbarObj } from "@/type/ComponentDataType"; import { Store, useStore } from "vuex";  // DOM操作,必須return否則不會(huì)生效 const messagesContainer = ref<HTMLDivElement | null>(null); const msgInputContainer = ref<HTMLDivElement | null>(null); const selectImg = ref<HTMLImageElement | null>(null); // 響應(yīng)式Data變量 const messageContent = ref<string>(""); const emoticonShowStatus = ref<string>("none"); const senderMessageList = reactive([]); const isBottomOut = ref<boolean>(true); let listId = ref<string>(""); let messageStatus = ref<number>(0); let buddyId = ref<string>(""); let buddyName = ref<string>(""); let serverTime = ref<string>(""); let emit: (event: string, ...args: any[]) => void = () => {   return 0; }; // store與當(dāng)前實(shí)例 let $store = useStore(); let currentInstance = getCurrentInstance();  export default function initData(): messageDisplayDataType {   // 定義set方法,將props中的數(shù)據(jù)寫入當(dāng)前實(shí)例   const setData = (     listIdParam: Ref<string>,     messageStatusParam: Ref<number>,     buddyIdParam: Ref<string>,     buddyNameParam: Ref<string>,     serverTimeParam: Ref<string>,     emitParam: (event: string, ...args: any[]) => void   ) => {     listId = listIdParam;     messageStatus = messageStatusParam;     buddyId = buddyIdParam;     buddyName = buddyNameParam;     serverTime = serverTimeParam;     emit = emitParam;   };   const setProperty = (     storeParam: Store<any>,     instanceParam: ComponentInternalInstance | null   ) => {     $store = storeParam;     currentInstance = instanceParam;   };      // 返回組件需要的Data   return {     messagesContainer,     msgInputContainer,     selectImg,     $store,     emoticonShowStatus,     currentInstance,     // .... 其他部分省略....     emit   } }

??細(xì)心的開發(fā)者可能已經(jīng)發(fā)現(xiàn),我把響應(yīng)式變量定義在導(dǎo)出的函數(shù)外面了,之所以這么做是因?yàn)閟etup的一些特殊原因,在下面的踩坑章節(jié)我將會(huì)詳解我為什么要這樣做。

在組件中使用

定義完相應(yīng)死變量后,我們就可以在組件中導(dǎo)入使用了,部分代碼如下所示,完整代碼請移步:message-display.vue

import initData from "@/module/message-display/main-entrance/InitData";  export default defineComponent({    setup(props, context) {     // 初始化組件需要的data數(shù)據(jù)     const {       createDisSrc,       resourceObj,       messageContent,       emoticonShowStatus,       emojiList,       toolbarList,       senderMessageList,       isBottomOut,       audioCtx,       arrFrequency,       pageStart,       pageEnd,       pageNo,       pageSize,       sessionMessageData,       msgListPanelHeight,       isLoading,       isLastPage,       msgTotals,       isFirstLoading,       messagesContainer,       msgInputContainer,       selectImg     } = initData();           // 返回組件需要用到的方法     return {       createDisSrc,       resourceObj,       messageContent,       emoticonShowStatus,       emojiList,       toolbarList,       senderMessageList,       isBottomOut,       audioCtx,       arrFrequency,       pageStart,       pageEnd,       pageNo,       pageSize,       sessionMessageData,       msgListPanelHeight,       isLoading,       isLastPage,       msgTotals,       isFirstLoading,       messagesContainer,       msgInputContainer,       selectImg     };    } })

我們定義后響應(yīng)式變量后,就可以在拆分出來的文件中導(dǎo)入initData函數(shù),訪問里面存儲(chǔ)的變量了。

在文件中訪問initData

我將頁面內(nèi)所有的事件監(jiān)聽也拆分成了文件,放在了EventMonitoring.ts中,在事件監(jiān)聽的處理函數(shù)是需要訪問initData里存儲(chǔ)的變量的,接下來我們就來看下如何訪問,部分代碼如下所示,完整代碼請移步EventMonitoring.ts)

import {   computed,   Ref,   ComputedRef,   watch,   getCurrentInstance,   toRefs } from "vue"; import { useStore } from "vuex"; import initData from "@/module/message-display/main-entrance/InitData"; import { SetupContext } from "@vue/runtime-core"; import _ from "lodash";   export default function eventMonitoring(   props: messageDisplayPropsType,   context: SetupContext<any> ): {   userID: ComputedRef<string>;   onlineUsers: ComputedRef<number>; } | void {   const $store = useStore();   const currentInstance = getCurrentInstance();   // 獲取傳遞的參數(shù)   const data = initData();   // 將props改為響應(yīng)式   const prop = toRefs(props);   // 獲取data中的數(shù)據(jù)   const senderMessageList = data.senderMessageList;   const sessionMessageData = data.sessionMessageData;   const pageStart = data.pageStart;   const pageEnd = data.pageEnd;   const pageNo = data.pageNo;   const isLastPage = data.isLastPage;   const msgTotals = data.msgTotals;   const msgListPanelHeight = data.msgListPanelHeight;   const isLoading = data.isLoading;   const isFirstLoading = data.isFirstLoading;   const listId = data.listId;   const messageStatus = data.messageStatus;   const buddyId = data.buddyId;   const buddyName = data.buddyName;   const serverTime = data.serverTime;   const messagesContainer = data.messagesContainer as Ref<HTMLDivElement>;      // 監(jiān)聽listID改變   watch(prop.listId, (newMsgId: string) => {     listId.value = newMsgId;     messageStatus.value = prop.messageStatus.value;     buddyId.value = prop.buddyId.value;     buddyName.value = prop.buddyName.value;     serverTime.value = prop.serverTime.value;     // 消息id發(fā)生改變,清空消息列表數(shù)據(jù)     senderMessageList.length = 0;     // 初始化分頁數(shù)據(jù)     sessionMessageData.length = 0;     pageStart.value = 0;     pageEnd.value = 0;     pageNo.value = 1;     isLastPage.value = false;     msgTotals.value = 0;     msgListPanelHeight.value = 0;     isLoading.value = false;     isFirstLoading.value = true;   }); }

正如代碼中那樣,在文件中使用時(shí),拿出initData中對應(yīng)的變量,需要修改其值時(shí),只需要修改他的value即可。

至此,有關(guān)compositionAPI的基本使用就跟大家講解完了,下面將跟大家分享下我在實(shí)現(xiàn)過程中所踩的坑,以及我的解決方案。

踩坑分享

今天是周四,我周一開始決定使用CompositionAPI來重構(gòu)我這個(gè)組件的,一直搞到昨天晚上才重構(gòu)完成,前前后后踩了很多坑,正所謂踩坑越多你越強(qiáng),這句話還是很有道理的??。

接下來就跟大家分享下我踩到的一些坑以及我的解決方案。

dom操作

我的組件需要對dom進(jìn)行操作,在optionsAPI中可以使用this.$refs.xxx來訪問組件dom,在setup中是沒有this的,翻了下官方文檔后,發(fā)現(xiàn)需要通過ref來定義,如下所示:

<template> <div ref="msgInputContainer"></div> <ul v-for="(item, i) in list" :ref="el => { ulContainer[i] = el }"></ul> </template>  <script lang="ts">   import { ref, reactive, onBeforeUpdate } from "vue";   setup(){     export default defineComponent({     // DOM操作,必須return否則不會(huì)生效     // 獲取單一dom     const messagesContainer = ref<HTMLDivElement | null>(null);     // 獲取列表dom     const ulContainer = ref<HTMLUListElement>([]);     const list = reactive([1, 2, 3]);     // 列表dom在組件更新前必須初始化     onBeforeUpdate(() => {        ulContainer.value = [];     });     return {       messagesContainer,       list,       ulContainer     }   })   } </script>

訪問vuex

在setup中訪問vuex需要通過useStore()來訪問,代碼如下所示:

import { useStore } from "vuex";  const $store = useStore(); console.log($store.state.token);

訪問當(dāng)前實(shí)例

在組件中需要訪問掛載在globalProperties上的東西,在setup中就需要通過getCurrentInstance()來訪問了,代碼如下所示:

import { getCurrentInstance } from "vue";  const currentInstance = getCurrentInstance(); currentInstance?.appContext.config.globalProperties.$socket.sendObj({   code: 200,   token: $store.state.token,   userID: $store.state.userID,   msg: $store.state.userID + "上線" });

無法訪問$options

我重構(gòu)的websocket插件是將監(jiān)聽消息接收方法放在options上的,需要通過this.$options.xxx來訪問,文檔翻了一圈沒找到有關(guān)在setup中使用的內(nèi)容,那看來是不能訪問了,那么我只能選擇妥協(xié),把插件掛載在options上的方法放到globalProperties上,這樣問題就解決了。

內(nèi)置方法只能在setup中訪問

如上所述,我們使用到了getCurrentInstance和useStore,這兩個(gè)內(nèi)置方法還有initData中定義的那些響應(yīng)式數(shù)據(jù),只有在setup中使用時(shí)才能拿到數(shù)據(jù),否則就是null。

我的文件是拆分出去的,有些函數(shù)是運(yùn)行在某個(gè)拆分出來的文件中的,不可能都在setup中執(zhí)行一遍的,響應(yīng)式變量也不可能全當(dāng)作參數(shù)進(jìn)行傳遞的,為了解決這個(gè)問題,我有試過使用provide注入然后通過inject訪問,結(jié)果運(yùn)行后發(fā)現(xiàn)不好使,控制臺(tái)報(bào)黃色警告說provide和inject只能運(yùn)行在setup中,我直接裂開,當(dāng)時(shí)發(fā)了一條沸點(diǎn)求助了下,到了晚上也沒得到解決方案??。

經(jīng)過一番求助后,我的好友@前端印象給我提供了一個(gè)思路,成功的解決了這個(gè)問題,也就是我上面initData的做法,將響應(yīng)式變量定義在導(dǎo)出函數(shù)的外面,這樣我們在拆分出來的文件中導(dǎo)入initData方法時(shí),里面的變量都是指向同一個(gè)地址,可以直接訪問存儲(chǔ)在里面的變量且不會(huì)將其進(jìn)行初始化。

至于getCurrentInstance和useStore訪問出現(xiàn)null的情景,還有props、emit的使用問題,我們可以在initData的導(dǎo)出函數(shù)內(nèi)部定義set方法,在setup里的方法中獲取到實(shí)例后,通過set方法將其設(shè)置進(jìn)我們定義的變量中。

看完上述內(nèi)容,你們掌握Vue3中怎么利用CompositionAPI優(yōu)化代碼量的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI