标题提到的Vue 3.3在本文写作时(2022年7月)尚未正式发布(实际于2023年5月发布)。本文基于Vue 3.2的Composition API,结合Vue 3.3的新特性预期,分享一次真实的代码重构经历。
上个月,我接手了一个Vue项目。其中有一个组件,代码写得非常混乱:一个文件八百多行,模板、逻辑、样式混在一起,变量命名随意,复制粘贴严重,bug不断。
我花了一周时间,把这个组件从烂代码重构成了优雅、可维护的代码。本文分享这次重构的过程,包括发现的问题、重构的思路、具体的做法,以及重构后的效果。
一、重构前的烂代码
先看看重构前的代码是什么样的。
1. 代码结构混乱
这个组件是一个用户管理页面,包含:
- 用户列表展示
- 搜索和筛选
- 新增和编辑用户
- 删除用户
- 批量操作
- 权限控制
所有功能都写在一个文件里,模板部分就有两百多行,script部分有五百多行,style部分有一百多行。
打开文件,一眼看不到头,想找一个功能,要翻半天。
2. 变量命名随意
变量命名非常随意:
const a = ref([]) // 用户列表
const b = ref('') // 搜索关键词
const c = ref(false) // 弹窗显示
const d = ref({}) // 当前编辑的用户看到a、b、c、d,完全不知道是什么意思,要去看上下文才能猜出来。
3. 复制粘贴严重
新增用户和编辑用户的表单,几乎是一样的,但代码复制了两份。修改一个地方,要记得改另一个地方,经常改漏。
4. 逻辑全堆在setup里
所有逻辑都写在setup函数里,几百行代码,没有任何组织。搜索逻辑、表单逻辑、列表逻辑、权限逻辑,全混在一起。
想改一个搜索的bug,要在几百行代码里找相关的部分,很容易改错。
5. 没有类型
项目用了TypeScript,但几乎没有类型定义。所有变量都是any,IDE没有提示,编译不报错,运行时才出问题。
6. 魔法数字和字符串
代码里到处是魔法数字和字符串:
if (user.status === 1) { ... } // 1是什么意思?
if (user.role === 'admin') { ... } // 还有哪些角色?没有注释,没有常量,不知道这些数字和字符串代表什么。
二、重构的思路
面对这样的烂代码,我没有直接动手改,而是先想清楚重构的思路。
1. 先理解,再动手
重构的第一步,是理解代码。
我花了两天时间,把这个组件从头到尾读了一遍,搞清楚了每个功能的逻辑。然后画了一个功能结构图,把组件的功能拆分成了几个模块:
- 列表模块:数据获取、分页、排序
- 搜索模块:关键词搜索、条件筛选
- 表单模块:新增、编辑、表单验证
- 操作模块:删除、批量操作
- 权限模块:按钮权限、数据权限
2. 从小到大,逐步重构
重构不要一上来就全部重写,那样风险太大。
我的做法是从小到大,逐步重构:
- 先改变量命名,让代码可读
- 提取常量,消除魔法数字
- 拆分组件,把大组件拆成小组件
- 提取组合式函数(composables),把逻辑抽出来
- 加类型定义,让TypeScript发挥作用
- 最后优化模板和样式
每一步都很小,改完就测试,确保没有问题。
3. 保持功能不变
重构的原则是:功能不变,只改代码结构。
重构过程中,不要顺手加新功能,不要顺手改bug(除非是重构必须的)。否则,出了问题不知道是重构导致的,还是新功能导致的。
三、重构的具体做法
1. 第一步:改变量命名
先把所有无意义的变量名,改成有意义的名字:
// 重构前
const a = ref([])
const b = ref('')
const c = ref(false)
const d = ref({})
// 重构后
const userList = ref([])
const searchKeyword = ref('')
const dialogVisible = ref(false)
const currentUser = ref({})改完之后,代码的可读性大大提升。不用看上下文,就知道每个变量是干什么的。
2. 第二步:提取常量
把魔法数字和字符串,提取成常量:
// 重构前
if (user.status === 1) { ... }
if (user.role === 'admin') { ... }
// 重构后
const USER_STATUS = {
ACTIVE: 1,
DISABLED: 2,
PENDING: 3
}
const USER_ROLE = {
ADMIN: 'admin',
EDITOR: 'editor',
VIEWER: 'viewer'
}
if (user.status === USER_STATUS.ACTIVE) { ... }
if (user.role === USER_ROLE.ADMIN) { ... }提取常量后,代码更清晰,也更容易维护。以后要改状态值,只改一个地方就行。
3. 第三步:拆分组件
把大组件拆成小组件,是重构的关键一步。
我把用户管理页面拆成了以下组件:
UserManage.vue:主组件,负责整体布局和状态管理UserTable.vue:用户列表表格UserSearch.vue:搜索和筛选区域UserForm.vue:新增和编辑用户的表单UserBatchActions.vue:批量操作按钮ConfirmDialog.vue:确认弹窗
每个组件只负责一件事,代码量都不大,容易理解和维护。
拆分组件的原则:
- 单一职责:每个组件只做一件事
- 高内聚:相关的代码放在一起
- 低耦合:组件之间通过props和events通信,不要互相依赖内部实现
- 可复用:能复用的组件尽量复用
4. 第四步:提取组合式函数(composables)
组件拆分后,逻辑还是分散在各个组件里。这时候,用Composition API的组合式函数(composables),把相关的逻辑抽出来。
我提取了以下composables:
useUserList():用户列表的数据获取、分页、排序useUserSearch():搜索和筛选逻辑useUserForm():表单数据、验证、提交useUserActions():删除、批量操作等动作usePermission():权限控制
比如,useUserList:
// composables/useUserList.js
import { ref, onMounted } from 'vue'
import { fetchUserList } from '@/api/user'
export function useUserList() {
const list = ref([])
const loading = ref(false)
const pagination = ref({
page: 1,
pageSize: 10,
total: 0
})
const loadList = async (params = {}) => {
loading.value = true
try {
const res = await fetchUserList({
page: pagination.value.page,
pageSize: pagination.value.pageSize,
...params
})
list.value = res.data
pagination.value.total = res.total
} finally {
loading.value = false
}
}
const handlePageChange = (page) => {
pagination.value.page = page
loadList()
}
onMounted(() => {
loadList()
})
return {
list,
loading,
pagination,
loadList,
handlePageChange
}
}然后在组件中使用:
<script setup>
import { useUserList } from '@/composables/useUserList'
const { list, loading, pagination, loadList, handlePageChange } = useUserList()
</script>提取composables后,组件的代码变得非常简洁,逻辑也更清晰。而且,composables可以在多个组件中复用,减少了重复代码。
5. 第五步:加类型定义
项目用了TypeScript,但之前几乎没有类型。重构时,我给所有数据加了类型定义。
首先定义用户的类型:
// types/user.ts
export interface User {
id: number
name: string
email: string
phone: string
status: UserStatus
role: UserRole
createdAt: string
updatedAt: string
}
export enum UserStatus {
ACTIVE = 1,
DISABLED = 2,
PENDING = 3
}
export enum UserRole {
ADMIN = 'admin',
EDITOR = 'editor',
VIEWER = 'viewer'
}
export interface UserListParams {
page: number
pageSize: number
keyword?: string
status?: UserStatus
role?: UserRole
}
export interface UserListResponse {
data: User[]
total: number
}然后在composables中使用:
import { ref, onMounted } from 'vue'
import type { Ref } from 'vue'
import { fetchUserList } from '@/api/user'
import type { User, UserListParams, UserListResponse } from '@/types/user'
export function useUserList() {
const list: Ref<User[]> = ref([])
const loading = ref(false)
const pagination = ref({
page: 1,
pageSize: 10,
total: 0
})
const loadList = async (params: Partial<UserListParams> = {}) => {
// ...
}
return {
list,
loading,
pagination,
loadList
}
}加了类型之后,IDE有了智能提示,编译时能发现类型错误,代码质量大大提升。
6. 第六步:优化模板
模板也做了优化:
- 用
v-if和v-show合理控制显示 - 用计算属性处理复杂的模板逻辑
- 提取重复的模板为子组件
- 用插槽(slot)提高组件的灵活性
- 给元素加有意义的class名
比如,重构前的模板:
<div v-for="item in a" :key="item.id">
<span>{{ item.name }}</span>
<span v-if="item.status === 1">正常</span>
<span v-else>禁用</span>
</div>重构后:
<UserTable
:list="userList"
:loading="loading"
@edit="handleEdit"
@delete="handleDelete"
/>模板变得非常简洁,逻辑都移到了组件和composables里。
7. 第七步:优化样式
样式也做了优化:
- 用CSS变量管理颜色和间距
- 用BEM命名规范
- 提取公共样式
- 去掉无用的样式
- 用scoped样式,避免样式污染
四、重构后的效果
重构完成后,效果非常明显。
1. 代码量减少
重构前:一个文件800多行。 重构后:主组件100多行,加上各个子组件和composables,总共约600行。
代码量减少了25%,而且结构更清晰。
2. 可维护性提升
以前改一个功能,要在800行代码里找,很容易改错。 现在,每个功能都在对应的组件或composable里,找起来很方便。
新同事接手项目,也能很快理解代码结构。
3. bug减少
重构过程中,发现了好几个隐藏的bug:
- 复制粘贴导致的逻辑不一致
- 变量名相似导致的误用
- 没有类型导致的运行时错误
重构后,这些bug都修复了。而且,因为有了类型检查,新的bug也更容易发现。
4. 可复用性提升
提取的composables和子组件,可以在其他页面复用。比如,useUserForm在用户管理和个人中心都能用。
5. 开发效率提升
以前加一个功能,要在大文件里改,很容易出问题。 现在,只需要改对应的组件或composable,开发效率提升了很多。
五、重构的经验总结
这次重构,我总结了一些经验。
1. 重构不是重写
重构是在保持功能不变的前提下,改善代码结构。不要一上来就全部重写,那样风险太大。
要小步快跑,每一步都很小,改完就测试,确保没有问题。
2. 先理解,再动手
重构之前,一定要先理解代码。不知道代码在干什么,就不要动手改。
可以先画功能结构图,理清代码的逻辑,再开始重构。
3. 有测试再重构
如果有单元测试或集成测试,重构会安全很多。改完代码,跑一遍测试,就知道有没有问题。
如果没有测试,可以先写一些基本的测试,再开始重构。
4. 命名很重要
好的命名,是代码可读的基础。变量名、函数名、组件名,都要有意义,让人一看就知道是干什么的。
不要怕名字长,清晰比简短更重要。
5. 拆分是关键
大组件拆成小组件,大函数拆成小函数,这是重构的关键。
每个组件、每个函数只做一件事,代码就会变得清晰、可维护。
6. 类型是好东西
TypeScript的类型系统,能帮你发现很多错误。不要偷懒,给所有数据加类型。
刚开始可能觉得麻烦,但习惯之后,你会发现开发效率更高,bug更少。
7. 不要追求完美
重构不是一次就能做到完美的。先把最乱的地方改好,其他地方以后再慢慢优化。
代码质量是一个持续提升的过程,不要指望一次重构就解决所有问题。
六、Vue 3.3的新特性预期
虽然Vue 3.3还没发布,但根据目前的信息,有一些新特性值得期待:
- 更好的TypeScript支持:更完善的类型推导
- 宏的改进:defineProps、defineEmits的类型更强大
- 新的编译优化:更快的渲染速度
- 更好的开发体验:更友好的警告和错误信息
这些新特性,会让Vue的代码更优雅、更高效。等Vue 3.3发布后,可以进一步利用这些特性优化代码。
七、写在最后
这次代码重构,让我深刻体会到了好代码的重要性。
烂代码,改一个bug要半天,还容易引入新bug;好代码,改起来轻松,不容易出错。
重构虽然花了一周时间,但从长远来看,是值得的。以后维护这个组件,会轻松很多。
2022年了,Vue 3已经成为主流,Composition API让代码组织更灵活。但工具再好,也要写代码的人有好的习惯。
最后,用一句话总结:"烂代码是技术债,早还早轻松。重构不是重写,而是小步快跑,逐步改善。"
愿你的代码,优雅而可维护。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录