返回顶部
首页 > 资讯 > 前端开发 > VUE >Vue3中怎么利用CompositionAPI优化代码量
  • 477
分享到

Vue3中怎么利用CompositionAPI优化代码量

2024-04-02 19:04:59 477人浏览 独家记忆
摘要

vue3中怎么利用Compositionapi优化代码量,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。我们先来看看组件的整体代码结构,如下图所

vue3中怎么利用Compositionapi优化代码量,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

我们先来看看组件的整体代码结构,如下图所示:

Vue3中怎么利用CompositionAPI优化代码量

image-20210114095802363

  • template部分占用267行

  • script部分占用889行

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

罪魁祸首就是script部分,本文要优化的就是这一部分的代码,我们再来细看下script中的代码结构:

  • props部分占用6行

  • data部分占用52行

  • created部分占用8行

  • mounted部分占用98行

  • methods部分占用672行

  • emits部分占用6行

  • computed部分占用8行

  • watch部分占用26行

现在罪魁祸首是methods部分,那么我们只需要把methods部分的代码拆分出去,单文件代码量就大大减少了。

优化方案

经过上述分析后,我们已经知道了问题所在,接下来就跟大家分享下我一开始想到的方案以及最终所采用的方案。

直接拆分成文件

一开始我觉得既然methods方法占用的行数太多,那么我在src下创建一个methods文件夹,把每个组件中的methods的方法按照组件名进行划分,创建对应的文件夹,在对应的组件文件夹内部,将methods中的方法拆分成独立的ts文件,最后创建index.ts文件,将其进行统一导出,在组件中使用时按需导入index.ts中暴露出来的模块,如下图所示:

Vue3中怎么利用CompositionAPI优化代码量

image-20210114103824562

  • 创建methods文件夹

  • 把每个组件中的methods的方法按照组件名进行划分,创建对应的文件夹,即:message-display

  • 将methods中的方法拆分成独立的ts文件,即:message-display文件夹下的ts文件

  • 创建index.ts文件,即:methods下的index.ts文件

index.ts代码

如下所示,我们将拆分的模块方法进行导入,然后统一export出去

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

在组件中使用

最后,我们在组件中按需导入即可,如下所示:

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

运行结果

当我自信满满的开始跑项目时,发现浏览器的控制台报错了,提示我this未定义,突然间我意识到将代码拆分成文件后,this是指向那个文件的,并没有指向当前组件实例,当然可以将this作为参数传进去,但我觉得这样并不妥,用到一个方法就传一个this进去,会产生很多冗余代码,因此这个方案被我pass了。

使用mixins

前一个方案因为this的问题以失败告终,在Vue2.x的时候官方提供了mixins来解决this问题,我们使用mixin来定义我们的函数,最后使用mixins进行混入,这样就可以在任意地方使用了。

由于mixins是全局混入的,一旦有重名的mixin原来的就会被覆盖,所以这个方案也不合适,pass。

Vue3中怎么利用CompositionAPI优化代码量

image-20210114111746208

使用CompositionAPI

上述两个方案都不合适,那 么CompositionAPI就刚好弥补上述方案的短处,成功的实现了我们想要实现的需求。

我们先来看看什么是CompositionAPI,正如文档所述,我们可以将原先optionsAPI中定义的函数以及这个函数需要用到的data变量,全部归类到一起,放到setup函数里,功能开发完成后,将组件需要的函数和data在setup进行return。

setup函数在创建组件之前执行,因此它是没有this的,这个函数可以接收2个参数: 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来向父组件传递数据,props和context这两个参数正好解决了我这个问题。

setup又是个函数,也就意味着我们可以将所有的函数拆分成独立的ts文件,然后在组件中导入,在setup中将其return给组件即可,这样就很完美的实现了一开始我们一开始所说的的拆分。

实现思路

接下来的内容会涉及到响应性API,如果对响应式API不了解的开发者请先移步官方文档。

我们分析出方案后,接下来我们就来看看具体的实现路:

  • 在组件的导出对象中添加setup属性,传入props和context

  • 在src下创建module文件夹,将拆分出来的功能代码按组件进行划分

  • 将每一个组件中的函数进一步按功能进行细分,此处我分了四个文件夹出来

    • common-methods 公共方法,存放不需要依赖组件实例的方法

    • components-methods 组件方法,存放当前组件模版需要使用的方法

    • main-entrance 主入口,存放setup中使用的函数

    • 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文件夹,用于存放我们拆分出来的功能代码文件。

如下所示,为我创建好的目录,我的划分依据是将相同类别的文件放到一起,每个文件夹的所代表的含义已在实现思路进行说明,此处不作过多解释。

Vue3中怎么利用CompositionAPI优化代码量

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

看完上述内容,你们掌握Vue3中怎么利用CompositionAPI优化代码量的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网VUE频道,感谢各位的阅读!

--结束END--

本文标题: Vue3中怎么利用CompositionAPI优化代码量

本文链接: https://lsjlt.com/news/72002.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Vue3中怎么利用CompositionAPI优化代码量
    Vue3中怎么利用CompositionAPI优化代码量,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。我们先来看看组件的整体代码结构,如下图所...
    99+
    2024-04-02
  • Vue3如何用CompositionAPI优化代码量
    这篇文章主要介绍了Vue3如何用CompositionAPI优化代码量的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3如何用CompositionAPI优化代码量文章都会有所收获,下面我们一起来看看吧。我...
    99+
    2023-07-04
  • 怎么利用优化JS代码来进行网站优化
    怎么利用优化JS代码来进行网站优化,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。现在优化JS代码也是我们在进行网站优化时经常使用的手段,js在数据传输,用户交互以及增进页面...
    99+
    2023-06-07
  • vue3中怎么用onLoad(代码实例)
    随着Vue 3的推出,开发者们也需要重新整合他们的技能和知识。在Vue 2中,onLoad是钩子函数之一,它被用于在组件初始化时执行一些逻辑代码。然而,在Vue 3中,onLoad钩子函数已经被废除。那么,在Vue 3中如何使用onLoad...
    99+
    2023-05-14
  • 怎么优化SQL代码
    这篇文章主要介绍“怎么优化SQL代码”,在日常操作中,相信很多人在怎么优化SQL代码问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么优化SQL代码”的疑惑有所帮助!接下来,...
    99+
    2024-04-02
  • 怎么优化JS代码
    这篇文章主要讲解了“怎么优化JS代码”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么优化JS代码”吧!1、字符串的拼接 字符串的拼接在我们开发中...
    99+
    2024-04-02
  • 利用反射机制优化Go语言代码
    利用反射机制优化 go 语言代码可以:获取类型信息(名称、类型、字段、方法):reflect.typeof()修改值(结构体、切片):reflect.valueof().elem().f...
    99+
    2024-04-08
    go 优化 go语言
  • 怎么用C语言优化Python代码
    这篇文章主要介绍“怎么用C优化Python代码”,在日常操作中,相信很多人在怎么用C优化Python代码问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用C优化Python代码”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-15
  • 如何优化Java代码中大量的if/else
    这篇文章主要介绍了如何优化Java代码中大量的if/else的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何优化Java代码中大量的if/else文章都会有所收获,下面我们一起来看看吧。观点一(灵剑)前期迭代...
    99+
    2023-07-05
  • 详解如何利用Python装饰器优化代码
    目录什么是装饰器装饰器的应用计时器装饰器缓存装饰器类型检查装饰器日志装饰器授权装饰器拓展高阶函数包装器总结本文将带你深入探讨装饰器的应用,包括计时器装饰器和缓存装饰器等的实现。通过这...
    99+
    2023-05-19
    Python装饰器优化代码 Python装饰器优化 Python装饰器
  • 怎么在Mysql中利用join优化sql
    怎么在Mysql中利用join优化sql?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。0. 准备相关表来进行接下来的测试user1表,取经...
    99+
    2024-04-02
  • SQLServer中怎么利用SETNOCOUNT优化存储
    SQLServer中怎么利用SETNOCOUNT优化存储,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。  SQLServer中怎样设置SE...
    99+
    2024-04-02
  • python中怎么使用管道Pipe编写优化代码
    本篇内容主要讲解“python中怎么使用管道Pipe编写优化代码”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“python中怎么使用管道Pipe编写优化代码”吧!我们知道 map 和 filte...
    99+
    2023-06-25
  • PHP中封装性的代码量监控与优化
    摘要:在PHP开发中,良好的封装性是增加代码可读性和维护性的关键。本文将介绍如何通过代码量监控和优化来提高PHP代码的封装性,并提供具体代码示例。引言:在现今的开发环境中,代码的可读性和维护性变得越来越重要。良好的封装性是保证代码质量的一个...
    99+
    2023-10-21
    优化 封装性 代码量
  • 怎么优化if-else代码结构
    本篇内容主要讲解“怎么优化if-else代码结构”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么优化if-else代码结构”吧!需求写一个 returnWee...
    99+
    2024-04-02
  • mysql 中怎么优化变量
    这篇文章给大家介绍mysql 中怎么优化变量,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 [d] port = 3306 server-id = 1  socket...
    99+
    2024-04-02
  • 怎么在Android应用中利用ViewHolder优化Adapter
    怎么在Android应用中利用ViewHolder优化Adapter?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。具体方法如下:public class MarkerItemA...
    99+
    2023-05-31
    adapter viewholder android
  • 一招教你优化Java代码中大量的if/else
    目录观点一(灵剑)1. 提前return2. 策略模式2.1 多态2.2 枚举3. 学会使用 Optional4. 数组小技巧观点二(IT技术控)观点一(灵剑) 前期迭代懒得优化,来...
    99+
    2023-03-24
    Java优化if else Java if else
  • Linux程序设计代码怎么优化
    本篇内容主要讲解“Linux程序设计代码怎么优化”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux程序设计代码怎么优化”吧!全局变量VS函数参数全局变量在Linux下的驱动编程里边,用的是...
    99+
    2023-06-16
  • Vue中怎么利用prerender-spa-plugin做SEO优化
    这篇文章给大家介绍Vue中怎么利用prerender-spa-plugin做SEO优化,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、安装插件使用下面命令安装对应插件npm ...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作