navInfos,
addTabShow,
isShowSaveAlert,
closeSaveConfigAlert,
showSaveConfigAlert,
isShowImportAlert,
showImportConfigAlert,
closeImportConfigAlert,
showSearch,
toID
}
}
}
上述代码是我项目中侧边栏中所有的变量以及方法,虽说变量和方法都同时存在于setup
函数中了,但是仍看起来杂乱无章,若是这个组件的业务需求越来越复杂,这个setup
内的代码可能更乱了
于是,我便开始构思如何抽离我的代码。后来在掘金的沸点上说了一下我的思路,并且询问了一下其他掘友的建议
其实最后一位老哥的回答对我启发很大,因此我也借鉴了一下它的思路对我的项目代码进行了抽离
=============================================================
首先我得思考一个问题:抽离代码时,是按照组件单独抽离?还是按照整体功能抽离?
最后我决定按照整体的功能去抽离代码,具体功能列表如下:
-
搜索功能
-
新增/修改标签功能
-
新增/修改网址功能
-
导入配置功能
-
导出配置功能
-
编辑功能
===============================================================
上述的每一个功能都会通过一个JS
文件去存储该功能对应的变量以及方法。然后所有的JS
文件都是放在src/use
下的,如图
就拿 新增/修改标签功能 来举例子,用一个动图给大家看看该功能的全部效果
很明显,我是做了一个弹窗组件,当点击侧边栏中的 + 号后,弹窗显示;然后我输入了想要新增标签的名称,并且选择了合适的图标,最后点击了确认,于是一个标签就添加好了,弹窗也随之隐藏;
最后我又去编辑模式下点击修改标签,弹窗再次显示,与此同时把对应标签的名称与图标都渲染了出来;待我修改了名字后,点击了确认,于是标签的信息就被我改好了,弹窗又随之隐藏了。
所以总结以下涉及到的功能就有以下几个:
-
弹窗的展示
-
弹窗的隐藏
-
点击确认后新增或修改标签内容
按照传统的写法,实现上述三个功能是这个样子的(我修改并简化了代码,大家理解意思就行):
- 侧边栏组件内容
<tab-alert :isShow=“isShow” @closeTabAlert=“close”/>
- 标签弹框组件内容
看完了我上面举例的代码后可以发现,简简单单的一个功能的实现,却涉及到两个组件,而且还需要父子组件相互通信来控制一些状态,这样不就把功能打散了嘛,即不够聚合。所以按照功能来抽离这些功能代码时,我会为他们创建一个 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
即可
<button @click=“log1”>打印加1
<button @click=“log2”>打印加2
然后再来说说为什么变量的定义在我们导出函数的外部。再继续看我上面举的我项目中标签页功能的例子吧,用于存储标签弹框展示状态的变量isShow
是在某个组件中定义的,同时标签组件也需要获取这个变量来控制展示的状态,这之间用到了父子组件通信,那么我们不妨把这个变量写在一个公共的文件中,无论哪个组件需要用到的时候,只需要导入获取就好了,因为每次获取到的都是同一个变量
这样一来,岂不是连父子组件通信都省了嘛?
我们把刚刚封装好的tabAlert.js
用到组件中去,看看是什么效果
- 侧边栏组件内容
- 标签弹框组件内容
这时候再翻上去看看最初的代码,有没有感觉代码抽离后,变得非常规整,而且组件中少了很多的代码量。
这样通过功能来将变量和代码聚集在一起的方法,我个人认为是比较好管理的,倘若之后有一天想在该功能上新增什么小需求,只要找到tabAlert.js
这个文件,在里面写方法和变量即可
=============================================================
我就是按照这样的方法,对我原本的代码进行了抽离,下面给大家看几组抽离前和抽离后的代码对比
- 抽离前
上传配置文件
<input id=“import_config_input” type=“file” class=“select-file” ref=“inputFile” @change=“fileChange”>
<lp-button type=“primary” class=“import-config-btn” @_click=“importConfig”>确认上传
- 抽离后
上传配置文件
<input id=“import_config_input” type=“file” class=“select-file” ref=“inputFile” @change=“fileChange”>
<lp-button type=“primary” class=“import-config-btn” @_click=“importConfig”>确认上传
- 抽离出的代码文件
// 导入配置功能
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 = value
if(!value) hasFile.value = 0
}
// 上传的文件内容发生改变
function fileChange(e) {
let files = e.target.files
if(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.files
if(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.result
window.localStorage.navInfos = data
location.reload()
}
}
}
return {
isShowImportAlert,
result,
isUpload,
isImport,
isLoading,
inputFile,
hasFile,
handleImportConfigAlert,
fileChange,
importConfig,
}
}
- 抽离前