网站首页 > 知识剖析 正文
在日常开发中,我们经常会遇到需要修改同事代码的情况。有时可能会花费很长时间却只改动了几行代码,而且改完后还可能引发新的bug。我们聊聊导致代码难以维护的常见原因,以及相应的解决方案。
常见问题及解决方案
1. 单文件代码过长
问题描述:
- 单个文件动辄几千行代码
- 包含大量DOM结构、JS逻辑和样式
- 需要花费大量时间才能理解代码结构
解决方案:
将大文件拆分成多个小模块,每个模块负责独立的功能。
以一个品牌官网为例,可以这样拆分:
<template>
<div>
<Header/>
<main>
<Banner/>
<AboutUs/>
<Services/>
<ContactUs/>
</main>
<Footer/>
</div>
</template>
2. 模块耦合严重
问题描述:
- 模块之间相互依赖
- 修改一处可能影响多处
- 难以进行单元测试
? 错误示例:
<script>
export default {
methods: {
getUserDetail() {
// 错误示范:多处耦合
let userId = this.$store.state.userInfo.id
|| window.currentUserId
|| this.$route.params.userId;
getUser(userId).then(res => {
// 直接操作子组件内部数据
this.$refs.userBaseInfo.data = res.baseInfo;
this.$refs.userArticles.data = res.articles;
})
}
}
}
</script>
? 正确示例:
<template>
<div>
<userBaseInfo :base-info="baseInfo"/>
<userArticles :articles="articles"/>
</div>
</template>
<script>
export default {
props: ['userId'],
data() {
return {
baseInfo: {},
articles: []
}
},
methods: {
getUserDetail() {
getUser(this.userId).then(res => {
this.baseInfo = res.baseInfo;
this.articles = res.articles;
})
}
}
}
</script>
3. 职责不单一
问题描述:
- 一个方法承担了多个功能
- 代码逻辑混杂在一起
- 难以复用和维护
? 错误示例:
<script>
export default {
methods: {
getUserData() {
userService.getUserList().then(res => {
this.userData = res.data;
// 一个方法中做了太多事情
let vipCount = 0;
let activeVipsCount = 0;
let activeUsersCount = 0;
this.userData.forEach(user => {
if(user.type === 'vip') {
vipCount++
}
if(dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))) {
if(user.type === 'vip') {
activeVipsCount++
}
activeUsersCount++
}
})
this.vipCount = vipCount;
this.activeVipsCount = activeVipsCount;
this.activeUsersCount = activeUsersCount;
})
}
}
}
</script>
? 正确示例:
<script>
export default {
computed: {
// 将不同统计逻辑拆分为独立的计算属性
activeUsers() {
return this.userData.filter(user =>
dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))
)
},
vipCount() {
return this.userData.filter(user => user.type === 'vip').length
},
activeVipsCount() {
return this.activeUsers.filter(user => user.type === 'vip').length
},
activeUsersCount() {
return this.activeUsers.length
}
},
methods: {
getUserData() {
// 方法只负责获取数据
userService.getUserList().then(res => {
this.userData = res.data;
})
}
}
}
</script>
<顺便吆喝一句,技术大厂内tui,前后端测试可投「链接」>
4. 代码复制代替复用
问题描述:
- 发现相似功能就直接复制代码
- 维护时需要修改多处相同的代码
- 容易遗漏修改点,造成bug
解决方案:
- 提前抽取公共代码
- 将重复逻辑封装成独立函数或组件
- 通过参数来处理细微差异
5. 强行复用/假装复用
问题描述:
将不该复用的代码强行糅合在一起,比如:
- 将登录弹窗和修改密码弹窗合并成一个组件
- 把一个实体的所有操作(增删改查)都塞进一个方法
? 错误示例:
<template>
<div>
<UserManagerDialog ref="UserManagerDialog"/>
</div>
</template>
<script>
export default {
methods: {
addUser() {
this.$refs.UserManagerDialog.showDialog({
type: 'add'
})
},
editName() {
this.$refs.UserManagerDialog.showDialog({
type: 'editName'
})
},
deleteUser() {
this.$refs.UserManagerDialog.showDialog({
type: 'delete'
})
}
}
}
</script>
? 正确做法:
- 不同业务逻辑使用独立组件
- 只抽取真正可复用的部分(如表单验证规则、公共UI组件等)
- 保持每个组件职责单一
6. 破坏数据一致性
问题描述: 使用多个关联状态来维护同一份数据,容易造成数据不一致。
? 错误示例:
<script>
export default {
data() {
return {
sourceData: [], // 原始数据
tableData: [], // 过滤后的数据
name: '', // 查询条件
type: ''
}
},
methods: {
nameChange(name) {
this.name = name;
// 手动维护 tableData,容易遗漏
this.tableData = this.sourceData.filter(item =>
(!this.name || item.name === this.name) &&
(!this.type || item.type === this.type)
);
},
typeChange(type) {
this.type = type;
// 重复的过滤逻辑
this.tableData = this.sourceData.filter(item =>
(!this.name || item.name === this.name) &&
(!this.type || item.type === this.type)
);
}
}
}
</script>
? 正确示例:
<script>
export default {
data() {
return {
sourceData: [],
name: '',
type: ''
}
},
computed: {
// 使用计算属性自动维护派生数据
tableData() {
return this.sourceData.filter(item =>
(!this.name || item.name === this.name) &&
(!this.type || item.type === this.type)
)
}
}
}
</script>
7. 解决方案不“正统”
问题描述:
使用不常见或不合理的方案解决问题,如:
直接修改 node_modules 中的代码,更好的实践:
- 优先使用框架/语言原生解决方案
- 遵循最佳实践和设计模式
- 进行方案评审和代码审查
- 对于第三方库的 bug:
- 向作者提交 issue 或 PR
- 将修改后的包发布到企业内部仓库
- *寻找替代方案
使用 JS 实现纯 CSS 可实现的效果
? 错误示例:
// 不恰当的鼠标悬停效果实现
element.onmouseover = function() {
this.style.color = 'red';
}
element.onmouseout = function() {
this.style.color = 'black';
}
? 正确示例:
/* 使用 CSS hover 伪类 */
.element:hover {
color: red;
}
过度使用全局变量
如何进行代码重构
重构的原则
- 不改变软件功能
- 小步快跑,逐步改进
- 边改边测试
- 随时可以暂停
重构示例
以下展示如何一步步重构上面的统计代码:
第一步:抽取 vipCount
- 删除data中的vipCount
- 增加计算属性vipCount,将getUserData中关于vipCount的逻辑挪到这里
- 删除getUserData中vipCount的计算逻辑
<script>
export default {
computed: {
vipCount() {
return this.userData.filter(user => user.type === 'vip').length
}
},
methods: {
getUserData() {
userService.getUserList().then(res => {
this.userData = res.data;
let activeVipsCount = 0;
let activeUsersCount = 0;
this.userData.forEach(user => {
if(dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))) {
if(user.type === 'vip') {
activeVipsCount++
}
activeUsersCount++
}
})
this.activeVipsCount = activeVipsCount;
this.activeUsersCount = activeUsersCount;
})
}
}
}
</script>
完成本次更改后,测试下各项数据是否正常,不正常查找原因,正常我们继续。
第二步:抽取 activeVipsCount
- 删除data中的activeVipsCount
- 增加计算属性activeVipsCount,将getUserData中activeVipsCount的计算逻辑迁移过来
- 删除getUserData中关于activeVipsCount计算的代码
<script>
export default {
computed: {
vipCount() {
return this.userData.filter(user => user.type === 'vip').length
},
activeVipsCount() {
return this.userData.filter(user =>
user.type === 'vip' &&
dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))
).length
}
},
methods: {
getUserData() {
userService.getUserList().then(res => {
this.userData = res.data;
let activeUsersCount = 0;
this.userData.forEach(user => {
if(dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))) {
activeUsersCount++
}
})
this.activeUsersCount = activeUsersCount;
})
}
}
}
</script>
最终版本:
<script>
export default {
computed: {
activeUsers() {
return this.userData.filter(user =>
dayjs(user.loginTime).isAfter(dayjs().subtract(30, 'day'))
)
},
vipCount() {
return this.userData.filter(user => user.type === 'vip').length
},
activeVipsCount() {
return this.activeUsers.filter(user => user.type === 'vip').length
},
activeUsersCount() {
return this.activeUsers.length
}
},
methods: {
getUserData() {
userService.getUserList().then(res => {
this.userData = res.data;
})
}
}
}
</script>
总结
要写出易维护的代码,需要注意:
1. 合理拆分模块,避免单文件过大
2. 降低模块间耦合
3. 保持职责单一
4. 使用计算属性处理派生数据
5. 定期进行代码重构
记住:重构是一个渐进的过程,不要试图一次性完成所有改进。在保证功能正常的前提下,通过小步快跑的方式逐步优化代码质量。
作者:Cyrus丶
猜你喜欢
- 2024-12-16 值得使用的CSS库添加图像悬停效果!
- 2024-12-16 HTML+CSS 二级导航 二级导航栏css代码怎么显示
- 2024-12-16 前端新手必看!HTML、CSS 和 JavaScript 详解与实用案例全攻略
- 2024-12-16 自制HTML游戏网页 html游戏制作教程
- 2024-12-16 纯CSS实现鼠标悬停提示的方法(CSS小白第三期)
- 2024-12-16 Day10_CSS过度动画 css过度和动画的区别
- 2024-12-16 伪元素的妙用2 - 多列均匀布局及title属性效果
- 2024-12-16 源自codepen的25个最受欢迎的HTML/CSS 代码
- 2024-12-16 如何处理pc和移动的hover? 移动pc端指的是什么意思啊
- 2024-12-16 前端纯CSS3实现图片Logo鼠标悬停(hover)光泽一闪而过的光影特效
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)