Maison > interface Web > Voir.js > Comment Vue3 utilise CompositionAPI pour optimiser la taille du code

Comment Vue3 utilise CompositionAPI pour optimiser la taille du code

WBOY
Libérer: 2023-05-28 16:55:23
avant
1151 Les gens l'ont consulté

Jetons d'abord un coup d'œil à la structure globale du code du composant :

  • La partie modèle occupe 267 lignes

  • la partie script occupe 889 lignes

  • La partie style occupe 1 ligne pour les références externes

Le coupable est la partie script, cet article va optimiser cette partie du code. Regardons de plus près la structure du code dans le script :

  • La partie props occupe 6 lignes

  • La partie données occupe 52 lignes.

  • La partie créée occupe 8 lignes

  • La partie montée occupe 98 lignes

  • La partie méthodes occupe 672 lignes

  • La partie émissions occupe 6 lignes

  • Le calcul La partie d occupe 8 lignes

  • La partie montre occupe 26 lignes

Maintenant, le coupable est la partie méthodes, il suffit donc de diviser le code dans la partie méthodes, et la quantité de code dans un seul fichier sera considérablement réduite.

Plan d'optimisation

Après l'analyse ci-dessus, nous connaissons déjà le problème. Ensuite, je partagerai avec vous le plan auquel j'ai initialement pensé et le plan que j'ai finalement adopté.

Divisez-le directement en fichiers

Au début, je pensais que comme la méthode méthodes prenait trop de lignes, j'avais créé un dossier méthodes sous src et divisé les méthodes dans chaque composant en fonction du nom du composant, créez le. dossier correspondant, à l'intérieur du dossier de composants correspondant, divisez les méthodes en méthodes en fichiers ts indépendants, créez enfin le fichier index.ts, exportez-le uniformément et importez l'index à la demande lors de son utilisation dans les modules de composants exposés dans .ts.

  • Créer un dossier de méthodes

  • Divisez les méthodes dans chaque composant en fonction du nom du composant et créez le dossier correspondant, à savoir : message-display

  • Divisez les méthodes dans les méthodes dans un fichier ts indépendant, c'est-à-dire : le fichier ts sous le dossier d'affichage des messages

  • Créez le fichier index.ts, c'est-à-dire : le fichier index.ts sous méthodes

le code index.ts

est le suivant, on importe le split méthodes du module, puis les exporter de manière unifiée

import compressPic from "@/methods/message-display/CompressPic"; import pasteHandle from "@/methods/message-display/PasteHandle";  export { compressPic, pasteHandle };
Copier après la connexion

Utilisation dans le composant

Enfin, nous pouvons l'importer dans le composant selon les besoins, comme indiqué ci-dessous :

import { compressPic, pasteHandle } from "@/methods/index";  export default defineComponent({     mounted() {       compressPic();       pasteHandle();     } })
Copier après la connexion

Résultats d'exécution

Quand j'ai commencé En exécutant le projet en toute confiance, j'ai constaté que la console du navigateur signalait une erreur, me signalant que cela n'était pas défini. Soudain, j'ai réalisé qu'après avoir divisé le code en fichiers, cela pointait vers ce fichier, pas vers ce fichier. transmettez-le en tant que paramètre à l'instance actuelle du composant, mais je pense que ce n'est pas approprié. Passer cela dans une méthode produira beaucoup de code redondant, j'ai donc adopté cette solution.

Utiliser les mixins

La solution précédente s'est soldée par un échec à cause de ce problème. Dans Vue2.x, les mixins ont été officiellement fournis pour résoudre ce problème. Nous utilisons des mixins pour définir nos fonctions, et enfin utilisons des mixins pour mélanger, donc. Il peut être utilisé n'importe où.

Étant donné que les mixins sont globalement mélangés, une fois qu'il y a un mixin du même nom, celui d'origine sera écrasé, donc cette solution ne convient pas, passez.

Utilisez CompositionAPI

Aucune des deux solutions ci-dessus ne convient, alors CompositionAPI compense simplement les lacunes des solutions ci-dessus et répond avec succès aux besoins que nous souhaitons atteindre.

Jetons d'abord un coup d'œil à ce qu'est CompositionAPI. Comme indiqué dans le document, nous pouvons regrouper toutes les fonctions définies dans l'optionsAPI d'origine et les variables de données que cette fonction doit utiliser dans la fonction de configuration, et le développement de la fonction est terminé. . Enfin, renvoyez les fonctions et les données requises par le composant dans la configuration.

La fonction de configuration est exécutée avant la création du composant, elle n'a donc pas cela. Cette fonction peut recevoir 2 paramètres : props et context Leurs définitions de type sont les suivantes :

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

Mon composant doit faire passer les props par le. composant parent La valeur in doit être transmise au composant parent via submit. Les deux paramètres props et context résolvent simplement ce problème pour moi.

setup est une fonction, ce qui signifie que nous pouvons diviser toutes les fonctions en fichiers ts indépendants, puis les importer dans le composant et les renvoyer au composant dans la configuration. C'est une implémentation parfaite. Commençons par le fractionnement dont nous avons parlé. environ au début.

Idées d'implémentation

Le prochain contenu impliquera l'API réactive. Si les développeurs ne comprennent pas l'API réactive, veuillez d'abord consulter la documentation officielle.

Après avoir analysé la solution, examinons le chemin d'implémentation spécifique :

  • Ajoutez l'attribut setup dans l'objet d'exportation du composant, transmettez les accessoires et le contexte

  • Créez le dossier du module sous src , divisez le code fonctionnel séparé en composants

  • et subdivisez ensuite les fonctions de chaque composant par fonction. Ici, je l'ai divisé en quatre dossiers

    • common-methods public method , stocke les méthodes qui n'ont pas besoin de s'appuyer sur un composant. instances

    • composants-méthodes méthodes de composants, stocke les méthodes qui doivent être utilisées par le modèle de composant actuel

    • entrée principale, stocke les fonctions utilisées dans la configuration

    • 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>
Copier après la connexion

创建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   } }
Copier après la connexion

??细心的开发者可能已经发现,我把响应式变量定义在导出的函数外面了,之所以这么做是因为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     };    } })
Copier après la connexion

我们定义后响应式变量后,就可以在拆分出来的文件中导入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;   }); }
Copier après la connexion

正如代码中那样,在文件中使用时,拿出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>
Copier après la connexion

访问vuex

在setup中访问vuex需要通过useStore()来访问,代码如下所示:

import { useStore } from "vuex";  const $store = useStore(); console.log($store.state.token);
Copier après la connexion

访问当前实例

在组件中需要访问挂载在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 + "上线" });
Copier après la connexion

无法访问$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方法将其设置进我们定义的变量中。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal