日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
使用Vue3的CompositionAPI來(lái)優(yōu)化代碼量

本文轉(zhuǎn)載自微信公眾號(hào)「神奇的程序員k」,作者神奇的程序員k 。轉(zhuǎn)載本文請(qǐng)聯(lián)系神奇的程序員k公眾號(hào)。

十多年的蘭坪網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷型網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整蘭坪建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。成都創(chuàng)新互聯(lián)公司從事“蘭坪網(wǎng)站設(shè)計(jì)”,“蘭坪網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

前言

在我的開(kāi)源項(xiàng)目中有一個(gè)組件是用來(lái)發(fā)送消息和展示消息的,這個(gè)組件的邏輯很復(fù)雜也是我整個(gè)項(xiàng)目的靈魂所在,單文件代碼有1100多行。我每次用webstorm編輯這個(gè)文件時(shí),電腦cpu溫度都會(huì)飆升并伴隨著卡頓。

就在前幾天我終于忍不住了,意識(shí)到了Vue2的optionsAPI的缺陷,決定用Vue3的CompositionAPI來(lái)解決這個(gè)問(wèn)題,本文就跟大家分享下我在優(yōu)化過(guò)程中踩到的坑以及我所采用的解決方案,歡迎各位感興趣的開(kāi)發(fā)者閱讀本文。

問(wèn)題分析

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

image-20210114095802363

  • template部分占用267行
  • script部分占用889行
  • style部分為外部引用占用1行

罪魁禍?zhǔn)拙褪莝cript部分,本文要優(yōu)化的就是這一部分的代碼,我們?cè)賮?lái)細(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)過(guò)上述分析后,我們已經(jīng)知道了問(wèn)題所在,接下來(lái)就跟大家分享下我一開(kāi)始想到的方案以及最終所采用的方案。

直接拆分成文件

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

image-20210114103824562

  • 創(chuàng)建methods文件夾
  • 把每個(gè)組件中的methods的方法按照組件名進(jìn)行劃分,創(chuàng)建對(duì)應(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出去

 
 
 
 
  1. import compressPic from "@/methods/message-display/CompressPic"; 
  2. import pasteHandle from "@/methods/message-display/PasteHandle"; 
  3.  
  4. export { compressPic, pasteHandle }; 

在組件中使用

最后,我們?cè)诮M件中按需導(dǎo)入即可,如下所示:

 
 
 
 
  1. import { compressPic, pasteHandle } from "@/methods/index"; 
  2.  
  3. export default defineComponent({ 
  4.     mounted() { 
  5.       compressPic(); 
  6.       pasteHandle(); 
  7.     } 
  8. }) 

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

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

使用mixins

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

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

image-20210114111746208

使用CompositionAPI

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

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

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

 
 
 
 
  1. interface Data { 
  2.   [key: string]: unknown 
  3.  
  4. interface SetupContext { 
  5.   attrs: Data 
  6.   slots: Slots 
  7.   emit: (event: string, ...args: unknown[]) => void 
  8. function setup(props: Data, context: SetupContext): Data 

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

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

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

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

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

  • 在組件的導(dǎo)出對(duì)象中添加setup屬性,傳入props和context
  • 在src下創(chuàng)建module文件夾,將拆分出來(lái)的功能代碼按組件進(jìn)行劃分
  • 將每一個(gè)組件中的函數(shù)進(jìn)一步按功能進(jìn)行細(xì)分,此處我分了四個(gè)文件夾出來(lái)
    • common-methods 公共方法,存放不需要依賴組件實(shí)例的方法
    • components-methods 組件方法,存放當(dāng)前組件模版需要使用的方法
    • main-entrance 主入口,存放setup中使用的函數(shù)
    • split-method 拆分出來(lái)的方法,存放需要依賴組件實(shí)例的方法,setup中函數(shù)拆分出來(lái)的文件也放在此處
  • 在主入口文件夾中創(chuàng)建InitData.ts文件,該文件用于保存、共享當(dāng)前組件需要用到的響應(yīng)式data變量
  • 所有函數(shù)拆分完成后,我們?cè)诮M件中將其導(dǎo)入,在setup中進(jìn)行return即可

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

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

添加setup選項(xiàng)

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

 
 
 
 
  1.  
  2.  

創(chuàng)建module模塊

我們?cè)趕rc下創(chuàng)建module文件夾,用于存放我們拆分出來(lái)的功能代碼文件。

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

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

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

 
 
 
 
  1. import { 
  2.   reactive, 
  3.   Ref, 
  4.   ref, 
  5.   getCurrentInstance, 
  6.   ComponentInternalInstance 
  7. } from "vue"; 
  8. import { 
  9.   emojiObj, 
  10.   messageDisplayDataType, 
  11.   msgListType, 
  12.   toolbarObj 
  13. } from "@/type/ComponentDataType"; 
  14. import { Store, useStore } from "vuex"; 
  15.  
  16. // DOM操作,必須return否則不會(huì)生效 
  17. const messagesContainer = ref(null); 
  18. const msgInputContainer = ref(null); 
  19. const selectImg = ref(null); 
  20. // 響應(yīng)式Data變量 
  21. const messageContent = ref(""); 
  22. const emoticonShowStatus = ref("none"); 
  23. const senderMessageList = reactive([]); 
  24. const isBottomOut = ref(true); 
  25. let listId = ref(""); 
  26. let messageStatus = ref(0); 
  27. let buddyId = ref(""); 
  28. let buddyName = ref(""); 
  29. let serverTime = ref(""); 
  30. let emit: (event: string, ...args: any[]) => void = () => { 
  31.   return 0; 
  32. }; 
  33. // store與當(dāng)前實(shí)例 
  34. let $store = useStore(); 
  35. let currentInstance = getCurrentInstance(); 
  36.  
  37. export default function initData(): messageDisplayDataType { 
  38.   // 定義set方法,將props中的數(shù)據(jù)寫(xiě)入當(dāng)前實(shí)例 
  39.   const setData = ( 
  40.     listIdParam: Ref
  41.     messageStatusParam: Ref
  42.     buddyIdParam: Ref
  43.     buddyNameParam: Ref
  44.     serverTimeParam: Ref
  45.     emitParam: (event: string, ...args: any[]) => void 
  46.   ) => { 
  47.     listId = listIdParam; 
  48.     messageStatus = messageStatusParam; 
  49.     buddyId = buddyIdParam; 
  50.     buddyName = buddyNameParam; 
  51.     serverTime = serverTimeParam; 
  52.     emit = emitParam; 
  53.   }; 
  54.   const setProperty = ( 
  55.     storeParam: Store
  56.     instanceParam: ComponentInternalInstance | null 
  57.   ) => { 
  58.     $store = storeParam; 
  59.     currentInstance = instanceParam; 
  60.   }; 
  61.    
  62.   // 返回組件需要的Data 
  63.   return { 
  64.     messagesContainer, 
  65.     msgInputContainer, 
  66.     selectImg, 
  67.     $store, 
  68.     emoticonShowStatus, 
  69.     currentInstance, 
  70.     // .... 其他部分省略.... 
  71.     emit 
  72.   } 

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

在組件中使用

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

 
 
 
 
  1. import initData from "@/module/message-display/main-entrance/InitData"; 
  2.  
  3. export default defineComponent({ 
  4.    setup(props, context) { 
  5.     // 初始化組件需要的data數(shù)據(jù) 
  6.     const { 
  7.       createDisSrc, 
  8.       resourceObj, 
  9.       messageContent, 
  10.       emoticonShowStatus, 
  11.       emojiList, 
  12.       toolbarList, 
  13.       senderMessageList, 
  14.       isBottomOut, 
  15.       audioCtx, 
  16.       arrFrequency, 
  17.       pageStart, 
  18.       pageEnd, 
  19.       pageNo, 
  20.       pageSize, 
  21.       sessionMessageData, 
  22.       msgListPanelHeight, 
  23.       isLoading, 
  24.       isLastPage, 
  25.       msgTotals, 
  26.       isFirstLoading, 
  27.       messagesContainer, 
  28.       msgInputContainer, 
  29.       selectImg 
  30.     } = initData(); 
  31.       
  32.     // 返回組件需要用到的方法 
  33.     return { 
  34.       createDisSrc, 
  35.       resourceObj, 
  36.       messageContent, 
  37.       emoticonShowStatus, 
  38.       emojiList, 
  39.       toolbarList, 
  40.       senderMessageList, 
  41.       isBottomOut, 
  42.       audioCtx, 
  43.       arrFrequency, 
  44.       pageStart, 
  45.       pageEnd, 
  46.       pageNo, 
  47.       pageSize, 
  48.       sessionMessageData, 
  49.       msgListPanelHeight, 
  50.       isLoading, 
  51.       isLastPage, 
  52.       msgTotals, 
  53.       isFirstLoading, 
  54.       messagesContainer, 
  55.       msgInputContainer, 
  56.       selectImg 
  57.     }; 
  58.    } 
  59. }) 

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

在文件中訪問(wèn)initData

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

 
 
 
 
  1. import { 
  2.   computed, 
  3.   Ref, 
  4.   ComputedRef, 
  5.   watch, 
  6.   getCurrentInstance, 
  7.   toRefs 
  8. } from "vue"; 
  9. import { useStore } from "vuex"; 
  10. import initData from "@/module/message-display/main-entrance/InitData"; 
  11. import { SetupContext } from "@vue/runtime-core"; 
  12. import _ from "lodash"; 
  13.  
  14.  
  15. export default function eventMonitoring( 
  16.   props: messageDisplayPropsType, 
  17.   context: SetupContext 
  18. ): { 
  19.   userID: ComputedRef
  20.   onlineUsers: ComputedRef
  21. } | void { 
  22.   const $store = useStore(); 
  23.   const currentInstance = getCurrentInstance(); 
  24.   // 獲取傳遞的參數(shù) 
  25.   const data = initData(); 
  26.   // 將props改為響應(yīng)式 
  27.   const prop = toRefs(props); 
  28.   // 獲取data中的數(shù)據(jù) 
  29.   const senderMessageList = data.senderMessageList; 
  30.   const sessionMessageData = data.sessionMessageData; 
  31.   const pageStart = data.pageStart; 
  32.   const pageEnd = data.pageEnd; 
  33.   const pageNo = data.pageNo; 
  34.   const isLastPage = data.isLastPage; 
  35.   const msgTotals = data.msgTotals; 
  36.   const msgListPanelHeight = data.msgListPanelHeight; 
  37.   const isLoading = data.isLoading; 
  38.   const isFirstLoading = data.isFirstLoading; 
  39.   const listId = data.listId; 
  40.   const messageStatus = data.messageStatus; 
  41.   const buddyId = data.buddyId; 
  42.   const buddyName = data.buddyName; 
  43.   const serverTime = data.serverTime; 
  44.   const messagesContainer = data.messagesContainer as Ref
  45.    
  46.   // 監(jiān)聽(tīng)listID改變 
  47.   watch(prop.listId, (newMsgId: string) => { 
  48.     listId.value = newMsgId; 
  49.     messageStatus.value = prop.messageStatus.value; 
  50.     buddyId.value = prop.buddyId.value; 
  51.     buddyName.value = prop.buddyName.value; 
  52.     serverTime.value = prop.serverTime.value; 
  53.     // 消息id發(fā)生改變,清空消息列表數(shù)據(jù) 
  54.     senderMessageList.length = 0; 
  55.     // 初始化分頁(yè)數(shù)據(jù) 
  56.     sessionMessageData.length = 0; 
  57.     pageStart.value = 0; 
  58.     pageEnd.value = 0; 
  59.     pageNo.value = 1; 
  60.     isLastPage.value = false; 
  61.     msgTotals.value = 0; 
  62.     msgListPanelHeight.value = 0; 
  63.     isLoading.value = false; 
  64.     isFirstLoading.value = true; 
  65.   }); 

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

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

踩坑分享

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

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

dom操作

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

 
 
 
 
  1.  
  2.  
  3.  

訪問(wèn)vuex

在setup中訪問(wèn)vuex需要通過(guò)useStore()來(lái)訪問(wèn),代碼如下所示:

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

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

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

 
 
 
 
  1. import { getCurrentInstance } from "vue"; 
  2.  
  3. const currentInstance = getCurrentInstance(); 
  4. currentInstance?.appContext.config.globalProperties.$socket.sendObj({ 
  5.   code: 200, 
  6.   token: $store.state.token, 
  7.   userID: $store.state.userID, 
  8.   msg: $store.state.userID + "上線" 
  9. }); 

無(wú)法訪問(wèn)$options

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

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

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

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

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

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

至此,問(wèn)題就完美解決了,最后跟大家看下優(yōu)化后的組件代碼,393行??

圖片

image-20210114201837539

項(xiàng)目地址

項(xiàng)目地址:chat-system-github

在線體驗(yàn)地址:chat-system


分享標(biāo)題:使用Vue3的CompositionAPI來(lái)優(yōu)化代碼量
網(wǎng)站鏈接:http://m.5511xx.com/article/cogiioj.html