Heim  >  Artikel  >  Web-Frontend  >  Wie Vue3 CompositionAPI verwendet, um die Codegröße zu optimieren

Wie Vue3 CompositionAPI verwendet, um die Codegröße zu optimieren

WBOY
WBOYnach vorne
2023-05-28 16:55:231104Durchsuche

Werfen wir zunächst einen Blick auf die gesamte Codestruktur der Komponente:

  • Der Vorlagenteil belegt 267 Zeilen

  • Der Skriptteil belegt 889 Zeilen

  • Der Stilteil belegt 1 Zeile für externe Referenzen

Der Übeltäter ist der Skriptteil. In diesem Artikel wird dieser Teil des Codes optimiert. Schauen wir uns die Codestruktur im Skript genauer an:

  • Der Requisitenteil belegt 6 Zeilen.

  • Der Datenteil belegt 52 Zeilen

  • Der erstellte Teil belegt 8 Zeilen

  • Der montierte Teil belegt 98 Zeilen

  • Der Methodenteil belegt 672 Zeilen

  • Der emittierte Teil belegt 6 Zeilen

  • Der berechnete Teil belegt 8 Zeilen

  • Der Überwachungsteil nimmt 26 Zeilen ein

Jetzt Der Übeltäter ist der Methodenteil, daher müssen wir nur den Code im Methodenteil aufteilen, wodurch die Codemenge in einer einzelnen Datei erheblich reduziert wird.

Optimierungsplan

Nach der obigen Analyse kennen wir das Problem bereits. Als nächstes werde ich Ihnen den Plan mitteilen, den ich ursprünglich gedacht und den ich schließlich angenommen habe.

Direkt in Dateien aufteilen

Zuerst dachte ich, dass ich einen Methodenordner unter src erstellt und die Methoden in jede Komponente entsprechend dem Komponentennamen aufgeteilt habe, um den entsprechenden Ordner zu erstellen Teilen Sie im entsprechenden Komponentenordner die Methoden in Methoden in unabhängige TS-Dateien auf, erstellen Sie schließlich die Datei index.ts, exportieren Sie sie einheitlich und importieren Sie den Index bei Bedarf, wenn Sie ihn in den in .ts bereitgestellten Komponentenmodulen verwenden.

  • Methodenordner erstellen

  • Unterteilen Sie die Methoden in jeder Komponente entsprechend dem Komponentennamen und erstellen Sie den entsprechenden Ordner, nämlich: message-display

  • Teilen Sie die Methoden in Methoden in unabhängige ts-Dateien auf, das heißt: Die TS-Datei im Nachrichtenanzeigeordner

  • Erstellen Sie die index.ts-Datei, das heißt: die index.ts-Datei unter Methoden

index.ts-Code

ist wie folgt, wir importieren die Aufteilung Modulmethoden und exportieren Sie sie dann auf einheitliche Weise.

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

Verwenden Sie sie in der Komponente Beim Ausführen des Projekts stellte ich fest, dass die Browserkonsole einen Fehler meldete, der mich darauf hinwies, dass dies undefiniert war. Nach der Aufteilung des Codes in Dateien wurde mir klar, dass dies auf diese Datei verwies, nicht auf diese Datei Übergeben Sie dies als Parameter an die aktuelle Komponenteninstanz, aber ich denke, dass dies nicht angemessen ist. Die Übergabe an eine Methode führt zu einer Menge redundantem Code, daher habe ich diese Lösung übergeben.

Verwenden Sie Mixins

Die vorherige Lösung scheiterte aufgrund dieses Problems. In Vue2.x wurden Mixins offiziell bereitgestellt, um unsere Funktionen zu definieren, und schließlich verwenden wir Mixins zum Einmischen Es kann überall verwendet werden.

Da Mixins global gemischt sind, wird das Original überschrieben, sobald ein Mixin mit demselben Namen vorhanden ist. Daher ist diese Lösung nicht geeignet. Passen Sie auf.

Verwenden Sie CompositionAPI

Keine der beiden oben genannten Lösungen ist geeignet, dann gleicht CompositionAPI lediglich die Mängel der oben genannten Lösungen aus und erfüllt erfolgreich die Anforderungen, die wir erreichen möchten.

Werfen wir zunächst einen Blick darauf, was CompositionAPI ist. Wie im Dokument angegeben, können wir alle in der ursprünglichen OptionsAPI definierten Funktionen und die Datenvariablen, die diese Funktion verwenden muss, in der Setup-Funktion gruppieren, und die Funktionsentwicklung ist abgeschlossen . Geben Sie abschließend die von der Komponente benötigten Funktionen und Daten im Setup zurück.

Die Setup-Funktion wird vor dem Erstellen der Komponente ausgeführt, sodass diese Funktion nicht über zwei Parameter verfügen kann: Requisiten und Kontext. Ihre Typdefinitionen lauten wie folgt:

import { compressPic, pasteHandle } from "@/methods/index";  export default defineComponent({     mounted() {       compressPic();       pasteHandle();     } })
Meine Komponente muss die von der übergebenen Requisiten erhalten Übergeordnete Komponente Der Wert in muss über emit an die übergeordnete Komponente übergeben werden. Die beiden Parameter props und context lösen dieses Problem für mich. setup ist eine Funktion, was bedeutet, dass wir alle Funktionen in unabhängige ts-Dateien aufteilen, sie dann in die Komponente importieren und im Setup an die Komponente zurückgeben können. Dies ist eine perfekte Implementierung, über die wir gesprochen haben etwa am Anfang.

Implementierungsideen

Der nächste Inhalt befasst sich mit der reaktionsfähigen API. Wenn Entwickler die reaktionsfähige API nicht verstehen, lesen Sie bitte zuerst die offizielle Dokumentation.

Nachdem wir die Lösung analysiert haben, werfen wir einen Blick auf den spezifischen Implementierungspfad:

Fügen Sie das Setup-Attribut zum Exportobjekt der Komponente hinzu, übergeben Sie Requisiten und Kontext.

Erstellen Sie den Modulordner unter src. Teilen Sie den getrennten Funktionscode in Komponenten auf

  • und unterteilen Sie die Funktionen in jeder Komponente weiter nach Funktion. Hier habe ich ihn in vier Ordner unterteilt

  • common-methods öffentliche Methode, speichert Methoden, die nicht auf Komponenten angewiesen sind Instanzen

  • components-methods Komponentenmethoden, speichert Methoden, die von der aktuellen Komponentenvorlage verwendet werden müssen

    • main-entry Haupteingang, speichert im Setup verwendete Funktionen

    • split-method 拆分出来的方法,存放需要依赖组件实例的方法,setup中函数拆分出来的文件也放在此处

  • 在主入口文件夹中创建InitData.ts文件,该文件用于保存、共享当前组件需要用到的响应式data变量

  • 所有函数拆分完成后,我们在组件中将其导入,在setup中进行return即可

实现过程

接下来我们将上述思路进行实现。

添加setup选项

我们在vue组件的导出部分,在其对象内部添加setup选项,如下所示:

<template>   <!---其他内容省略--> </template> <script lang="ts"> export default defineComponent({   name: "message-display",   props: {     listId: String, // 消息id     messageStatus: Number, // 消息类型     buddyId: String, // 好友id     buddyName: String, // 好友昵称     serverTime: String // 服务器时间   },   setup(props, context) {     // 在此处即可写响应性API提供的方法,注意⚠️此处不能用this   } } </script>

创建module模块

我们在src下创建module文件夹,用于存放我们拆分出来的功能代码文件。

创建InitData.ts文件

我们将组件中用到的响应式数据,统一在这里进行定义,然后在setup中进行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否则不会生效 const messagesContainer = ref<HTMLDivElement | null>(null); const msgInputContainer = ref<HTMLDivElement | null>(null); const selectImg = ref<HTMLImageElement | null>(null); // 响应式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与当前实例 let $store = useStore(); let currentInstance = getCurrentInstance();  export default function initData(): messageDisplayDataType {   // 定义set方法,将props中的数据写入当前实例   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   } }

??细心的开发者可能已经发现,我把响应式变量定义在导出的函数外面了,之所以这么做是因为setup的一些特殊原因,在下面的踩坑章节我将会详解我为什么要这样做。

在组件中使用

定义完相应死变量后,我们就可以在组件中导入使用了,部分代码如下所示,完整代码请移步:message-display.vue

import initData from "@/module/message-display/main-entrance/InitData";  export default defineComponent({    setup(props, context) {     // 初始化组件需要的data数据     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     };    } })

我们定义后响应式变量后,就可以在拆分出来的文件中导入initData函数,访问里面存储的变量了。

在文件中访问initData

我将页面内所有的事件监听也拆分成了文件,放在了EventMonitoring.ts中,在事件监听的处理函数是需要访问initData里存储的变量的,接下来我们就来看下如何访问,部分代码如下所示,完整代码请移步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();   // 获取传递的参数   const data = initData();   // 将props改为响应式   const prop = toRefs(props);   // 获取data中的数据   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>;      // 监听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发生改变,清空消息列表数据     senderMessageList.length = 0;     // 初始化分页数据     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;   }); }

正如代码中那样,在文件中使用时,拿出initData中对应的变量,需要修改其值时,只需要修改他的value即可。

至此,有关compositionAPI的基本使用就跟大家讲解完了,下面将跟大家分享下我在实现过程中所踩的坑,以及我的解决方案。

踩坑分享

今天是周四,我周一开始决定使用CompositionAPI来重构我这个组件的,一直搞到昨天晚上才重构完成,前前后后踩了很多坑,正所谓踩坑越多你越强,这句话还是很有道理的??。

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

dom操作

我的组件需要对dom进行操作,在optionsAPI中可以使用this.$refs.xxx来访问组件dom,在setup中是没有this的,翻了下官方文档后,发现需要通过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否则不会生效     // 获取单一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);

访问当前实例

在组件中需要访问挂载在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

我重构的websocket插件是将监听消息接收方法放在options上的,需要通过this.$options.xxx来访问,文档翻了一圈没找到有关在setup中使用的内容,那看来是不能访问了,那么我只能选择妥协,把插件挂载在options上的方法放到globalProperties上,这样问题就解决了。

内置方法只能在setup中访问

如上所述,我们使用到了getCurrentInstance和useStore,这两个内置方法还有initData中定义的那些响应式数据,只有在setup中使用时才能拿到数据,否则就是null。

我的文件是拆分出去的,有些函数是运行在某个拆分出来的文件中的,不可能都在setup中执行一遍的,响应式变量也不可能全当作参数进行传递的,为了解决这个问题,我有试过使用provide注入然后通过inject访问,结果运行后发现不好使,控制台报黄色警告说provide和inject只能运行在setup中,我直接裂开,当时发了一条沸点求助了下,到了晚上也没得到解决方案??。

经过一番求助后,我的好友@前端印象给我提供了一个思路,成功的解决了这个问题,也就是我上面initData的做法,将响应式变量定义在导出函数的外面,这样我们在拆分出来的文件中导入initData方法时,里面的变量都是指向同一个地址,可以直接访问存储在里面的变量且不会将其进行初始化。

至于getCurrentInstance和useStore访问出现null的情景,还有props、emit的使用问题,我们可以在initData的导出函数内部定义set方法,在setup里的方法中获取到实例后,通过set方法将其设置进我们定义的变量中。

Das obige ist der detaillierte Inhalt vonWie Vue3 CompositionAPI verwendet, um die Codegröße zu optimieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen