如何充分利用Composition API对Vue3项目进行代码抽离
Composition API的出现就是为了解决Options API导致相同功能代码分散的现象
看了一下我项目初版的代码,简直是没有体现出Composition API
的优势,可以给大家看一下某个组件内的代码
<template><aside id="tabs-container"><div id="logo-container">{{ navInfos.navName }}</div><ul id="tabs"><li class="tab tab-search" @click="showSearch"><i class="fas fa-search tab-icon"/><span>快速搜索</span></li><li class="tab tab-save" @click="showSaveConfigAlert"><i class="fas fa-share-square tab-icon"></i><span>保存配置</span></li><li class="tab tab-import" @click="showImportConfigAlert"><i class="fas fa-cog tab-icon"></i><span>导入配置</span></li><br><li v-for="(item, index) in navInfos.catalogue" :key="index"class="tab"@click="toID(item.id)"><span class="li-container"><i :class="['fas', `fa-${item.icon}`, 'tab-icon']" /><span>{{ item.name }}</span><i class="fas fa-angle-right tab-icon tab-angle-right"/></span></li><li class="tab add-tab" @click="addTabShow"><i class="fas fa-plus"/></li></ul><!-- 添加标签弹框 --><tabAlert /><!-- 保存配置弹框 --><save-config @closeSaveConfigAlert="closeSaveConfigAlert" :isShow="isShowSaveAlert"/><!-- 导入配置弹框 --><import-config @closeImportConfigAlert="closeImportConfigAlert" :isShow="isShowImportAlert"/></aside>
</template><script>
import {ref} from 'vue'
import {useStore} from 'vuex'
import tabAlert from '../public/tabAlert/tabAlert'
import saveConfig from './childCpn/saveConfig'
import importConfig from './childCpn/importConfig'
export default {name: 'tabs',components: {tabAlert,saveConfig,importConfig},setup() {const store = useStore() let navInfos = store.state // Vuex的state对象let isShowSaveAlert = ref(false) // 保存配置弹框是否展示let isShowImportAlert = ref(false) // 导入配置弹框是否展示// 展示"添加标签弹框"function addTabShow() {store.commit('changeTabInfo', [{key: 'isShowAddTabAlert', value: true},{key: 'alertType', value: '新增标签'}])}// 关闭"保存配置弹框"function closeSaveConfigAlert(value) {isShowSaveAlert.value = value}// 展示"保存配置弹框"function showSaveConfigAlert() {isShowSaveAlert.value = true}// 展示"导入配置弹框"function showImportConfigAlert() {isShowImportAlert.value = true}// 关闭"导入配置弹框"function closeImportConfigAlert(value) {isShowImportAlert.value = value}// 展示搜索框function showSearch() {if(store.state.moduleSearch.isSearch) {store.commit('changeIsSearch', false)store.commit('changeSearchWord', '')} else {store.commit('changeIsSearch', true)}}// 跳转到指定标签function toID(id) {const content = document.getElementById('content')const el = document.getElementById(`${id}`)let start = content.scrollToplet end = el.offsetTop - 80let each = start > end ? -1 * Math.abs(start - end) / 20 : Math.abs(start - end) / 20let count = 0let timer = setInterval(() => {if(count < 20) {content.scrollTop += eachcount ++} else {clearInterval(timer)}}, 10) }return {navInfos,addTabShow, isShowSaveAlert, closeSaveConfigAlert, showSaveConfigAlert,isShowImportAlert,showImportConfigAlert,closeImportConfigAlert,showSearch,toID}}
}
</script>
上述代码是我项目中侧边栏中所有的变量以及方法,虽说变量和方法都同时存在于setup
函数中了,但是仍看起来杂乱无章,若是这个组件的业务需求越来越复杂,这个setup
内的代码可能更乱了
于是,我便开始构思如何抽离我的代码。后来在掘金的沸点上说了一下我的思路,并且询问了一下其他掘友的建议
其实最后一位老哥的回答对我启发很大,因此我也借鉴了一下它的思路对我的项目代码进行了抽离
准备工作
首先我得思考一个问题:抽离代码时,是按照组件单独抽离?还是按照整体功能抽离?
最后我决定按照整体的功能去抽离代码,具体功能列表如下: 搜索功能 新增/修改标签功能 新增/修改网址功能 导入配置功能 导出配置功能
编辑功能
开始抽出代码
上述的每一个功能都会通过一个JS文件去存储该功能对应的变量以及方法。然后所有的JS文件都是放在src/use下的,如图
所以总结以下涉及到的功能就有以下几个:
弹窗的展示 弹窗的隐藏 点击确认后新增或修改标签内容 按照传统的写法,实现上述三个功能是这个样子的(我修改并简化了代码,大家理解意思就行):
侧边栏组件内容
<template><aside><div @click="show">新增标签</div><tab-alert :isShow="isShow" @closeTabAlert="close"/></aside>
</template><script>
import { ref } from 'vue'
import tabAlert from '@/components/tabAlert/index'
export default {name: "tab",components: {tabAlert},setup() {// 存储标签弹框的展示情况const isShow = ref(false) // 展示标签弹框function show() {isShow.value = true}// 隐藏标签弹框function close() {isShow.value = false}return { isShow, show, close }}
}
</script>
标签弹框组件内容
<template><div v-show="isShow"><!-- 此处省略一部分不重要的内容代码 --><div @click="close">取消</div><div @click="confirm">确认</div></div>
</template><script>
export default {name: "tab",props: {isShow: {type: Boolean,default: false}},setup(props, {emit}) {// 隐藏标签弹框function close() {emit('close')}// 点击确认后的操作function confirm() {/* 此处省略点击确认按钮后更新标签内容的业务代码 */close()}return { close, confirm }}
}
</script>
看完了我上面举例的代码后可以发现,简简单单的一个功能的实现,却涉及到两个组件,而且还需要父子组件相互通信来控制一些状态,这样不就把功能打散了嘛,即不够聚合。所以按照功能来抽离这些功能代码时,我会为他们创建一个 tabAlert.js 文件,里面存储着关于这个功能所有的变量与方法。
tabAlert.js
文件中的大致结构是这样的:
// 引入依赖API
import { ref } from 'vue'// 定义一些变量
const isShow = ref(false) // 存储标签弹框的展示状态export default function tabAlertFunction() {/* 定义一些方法 */// 展示标签弹框function show() {isShow.value = true}// 关闭标签弹框function close() {isShow.value = false}// 点击确认按钮以后的操作function confirm() {/* 此处省略点击确认按钮后更新标签内容的业务代码 */close()}return {isShow,show,close,confirm,}
}
对于为何设计这样的结构,先从导出的方法来说,我把跟该功能相关的所有方法放在了一个函数中,最后通过return导出,是因为有时候这些方法会依赖于外部其它的变量,所以用函数包裹了一层,例如:
// example.js
export default function exampleFunction(num) {function log1() {console.log(num + 1)}function log2() {console.log(num + 2)}return {log1,log2,}
}
从这个文件中我们发现,log1
和log2
方法都是依赖于变量num
的,但我们并没有在该文件中定义变量num
,那么可以在别的组件中引入该文件时,给最外层的exampleFunction
方法传递一个参数num
即可
<template><button @click="log1">打印加1</button><button @click="log2">打印加2</button>
</template><script>
import exampleFunction from './example'
import { num } from './getNum' // 假设num是从别的模块中获取到的
export default {setup() {let { log1, log2 } = exampleFunction(num)return { log1, log2 }}
}
</script>
然后再来说说为什么变量的定义在我们导出函数的外部。再继续看我上面举的我项目中标签页功能的例子吧,用于存储标签弹框展示状态的变量isShow是在某个组件中定义的,同时标签组件也需要获取这个变量来控制展示的状态,这之间用到了父子组件通信,那么我们不妨把这个变量写在一个公共的文件中,无论哪个组件需要用到的时候,只需要导入获取就好了,因为每次获取到的都是同一个变量
展示环节
对比1
抽离前
<template><div class="import-config-container" v-show="isShow"><div class="import-config-alert"><div class="close-import-config-alert" @click="closeAlert"></div><div class="import-config-alert-title">导入配置</div><div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div><form action="" class="form"><label for="import_config_input" class="import-config-label">上传配置文件<i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/><i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/></label><input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange"></form><lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button></div></div>
</template><script>
import {ref, inject} from 'vue'
import lpButton from '../../public/lp-button/lp-button'
export default {props: {isShow: {type: Boolean,default: true}},components: {lpButton},setup(props, {emit}) {const result = ref('none') // 导入的结果const isUpload = ref(false) // 判断是否上传配置文件const isImport = ref(false) // 判断配置是否导入成功const isLoading = ref(false) // 判断按钮是否处于加载状态const inputFile = ref(null) // 获取文件标签const hasFile = ref(0) // 判断文件的传入情况。0:未传入 1: 格式错误 2:格式正确const $message = inject('message')// 导入配置function importConfig() {let reader = new FileReader()let files = inputFile.value.filesif(hasFile.value == 0) {$message({type: 'warning',content: '请先上传配置文件'})}else if(hasFile.value == 1) {$message({type: 'warning',content: '请上传正确格式的文件,例如xx.json'})}else if(hasFile.value == 2) {reader.readAsText(files[0])reader.onload = function() {let data = this.resultwindow.localStorage.navInfos = datalocation.reload()}}}// 关闭弹窗function closeAlert() {emit('closeImportConfigAlert', false)hasFile.value = 0}function fileChange(e) {let files = e.target.filesif(files.length === 0) {$message({type: 'warning',content: '请先上传配置文件'})}else {let targetFile = files[0]if(!/\.json$/.test(targetFile.name)) {hasFile.value = 1$message({type: 'warning',content: '请确认文件格式是否正确'})} else {hasFile.value = 2$message({type: 'success',content: '文件格式正确'})}}}return {result, isUpload,isImport, isLoading,importConfig, closeAlert,inputFile,fileChange,hasFile}}
}
</script>
抽离后
<template><div class="import-config-container" v-show="isShowImportAlert"><div class="import-config-alert"><div class="close-import-config-alert" @click="handleImportConfigAlert(false)"></div><div class="import-config-alert-title">导入配置</div><div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div><form action="" class="form"><label for="import_config_input" class="import-config-label">上传配置文件<i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/><i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/></label><input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange"></form><lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button></div></div>
</template><script>
/* API */
import { inject } from 'vue'
/* 组件 */
import lpButton from '@/components/public/lp-button/lp-button'
/* 功能模块 */
import importConfigFunction from '@/use/importConfig'
export default {components: {lpButton},setup() {const $message = inject('message')const { isShowImportAlert,handleImportConfigAlert,result, isUpload, isImport, isLoading, importConfig, closeAlert, inputFile, fileChange, hasFile } = importConfigFunction($message)return {isShowImportAlert,handleImportConfigAlert,result, isUpload,isImport, isLoading,importConfig, closeAlert,inputFile,fileChange,hasFile}}
}
</script>
抽离出的代码文件
// 导入配置功能
import { ref } from 'vue'const isShowImportAlert = ref(false), // 导入配置弹框是否展示result = ref('none'), // 导入的结果isUpload = ref(false), // 判断是否上传配置文件isImport = ref(false), // 判断配置是否导入成功isLoading = ref(false), // 判断按钮是否处于加载状态inputFile = ref(null), // 获取文件元素hasFile = ref(0) // 判断文件的传入情况。0:未传入 1: 格式错误 2:格式正确export default function importConfigFunction($message) {// 控制弹框的展示function handleImportConfigAlert(value) {isShowImportAlert.value = valueif(!value) hasFile.value = 0}// 上传的文件内容发生改变function fileChange(e) {let files = e.target.filesif(files.length === 0) {$message({type: 'warning',content: '请先上传配置文件'})}else {let targetFile = files[0]if(!/\.json$/.test(targetFile.name)) {hasFile.value = 1$message({type: 'warning',content: '请确认文件格式是否正确'})} else {hasFile.value = 2$message({type: 'success',content: '文件格式正确'})}}}// 导入配置function importConfig() {let reader = new FileReader()let files = inputFile.value.filesif(hasFile.value == 0) {$message({type: 'warning',content: '请先上传配置文件'})}else if(hasFile.value == 1) {$message({type: 'warning',content: '请上传正确格式的文件,例如xx.json'})}else if(hasFile.value == 2) {reader.readAsText(files[0])reader.onload = function() {let data = this.resultwindow.localStorage.navInfos = datalocation.reload()}}}return {isShowImportAlert,result,isUpload,isImport,isLoading,inputFile,hasFile,handleImportConfigAlert,fileChange,importConfig,}
}
最后
本文所阐述的代码抽离方法是我改过很多遍后定下来的,不知道后面还会有什么问题,但目前看来,对于以后的维护和管理应该是会方便很多的,如果大家有更好的意见或想法,可以留下评论,或者加我vx:XFJ–123私底下交流
最后谢谢各位的耐心观看
写文章不容易,希望各位多多留言给我提提意见,别忘了点个赞
如何充分利用Composition API对Vue3项目进行代码抽离相关推荐
- Vue3.0 Composition API与Vue2.x 使用的 Options API
Vue3.0 所采用的 Composition API 与 Vue2.x 使用的 Options API 有什么不同 开始之前 Composition API 可以说是Vue3最大的特点,那么为什么要 ...
- onmounted vue3_Vue3.x 生命周期 和 Composition API 核心语法理解
1 Vue2.x 生命周期回顾 beforeCreate,在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用. created,在实例创建完 ...
- js 表单设计器_准备迎接Vue3,使用Vue Composition API生成干净可扩展的表单
表单是前端开发中最棘手的部分之一,您可能会在其中发现很多混乱的代码. Vue.js 2之类的基于组件的框架在提高前端代码的可伸缩性方面做了很多工作,但是形式问题仍然存在. 在本教程中,我将向您展示新的 ...
- 探秘 Vue3.0 - Composition API 在真实业务中的尝鲜姿势
前言 2019年2月6号,React 发布 16.8.0 版本,新增 Hooks 特性.随即,Vue 在 2019 的各大 JSConf 中也宣告了 Vue3.0 最重要的 RFC,即 Functio ...
- vue3.0 - Composition API
一. 介绍 >使用传统的option配置方法写组件的时候问题,随着业务复杂度越来越高,代码量会不断的加大:由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,同时 ...
- vue3.0 Composition API 上手初体验 使用 vue-router 构建多页面应用
vue3.0 Composition API 上手初体验 使用 vue-router 构建多页面应用 前两讲,我们已经顺利的使用 vue3.0 将项目跑起来了.但是实在是过于简陋,目前我们几乎不可能开 ...
- vue3基础(一 )composition api,typeof,keyof,keyof typeof
vue是渐进式框架 就是可以不全会,可以会一点用一点 vue3新特性: 破坏性变化: 迁移会出问题或者运行不了 移除: composition api 为vue应用提供更好的逻辑复用和代码组织 主要替 ...
- vue3学习笔记 Composition API setup
一.Composition API优势 相对于vue2的option API Vue3的Composition API设计更有优势 Composition(组合式)Api 功能分组 Compositi ...
- Vue3 Composition API(一)——setup、reactive、ref、readonly
一.Options API的弊端 在Vue2中,我们编写组件的方式是Options API: Options API的一大特点就是在对应的属性中编写对应的功能模块: 比如data定义数据.method ...
- Vue3笔记_02setup与常用的Composition API(组合式API)
目录 setup setup的执行时机 [1]定义数据和方法 vue2中定义数据和方法 vue3中定义数据和方法 语法 示例-返回值为一个对象 示例-返回值为一个渲染函数 注意点 数据-响应式数据 r ...
最新文章
- ThinkPHP 5.0 入门教程 一:安装ThinkPHP并在Web浏览器访问
- 谈谈UIView的几个layout方法-layoutSubviews、layoutIfNeeded、
- DOS命令输出的重定向
- 有了这套模板,女朋友再也不用担心我刷不动 LeetCode 了
- step2 . day1 Linux和C语言的高级应用
- 【2011-6】【奇数】
- js下载文件 java_[Java教程]使用js实现点击按钮下载文件
- 路由代码WebApi设置namespace路由参数
- C++ HOOK 详解
- java.util.concurrent.*下的常见类你了解多少?
- Git安装教程(windows)
- Objective-C 与JAVA的SHA1/HmacSHA1加密算法实现
- Web—09-正则表达式
- distinct 多列详解
- 计算机829大纲,829计算机基础考试大纲
- 玩转群晖NAS套件系列七:File Station的安装与使用保姆级教程!
- 写给即将毕业的同学们
- 习题6 3.6.2 典型题例解析 3.6.3 自测训练
- 写给后端开发看的安卓入门
- BAT和IBM信息无障碍现状概要
热门文章
- Android 神器 xposed 框架使用指南
- 解决桌面单击右键反应慢的问题
- pubg服务器维护6.23,pubg维护6月23日 | 手游网游页游攻略大全
- HDOJ-1823(矩形树,点更新 + 区间查询)
- Flink sql 写ddl连接kafka
- Oracle Spatial详解
- 采集用python还是火车头_火车采集器V9插件开发手册
- ping localhost
- Java日期时间主要API:java.time.Duration类和java.time.Period类
- 2021年7月最新iOS面试题总结(答案篇)