为了账号安全,请及时绑定邮箱和手机立即绑定

求放过!同事的前端代码我真的改不动了?

在日常开发中,我们经常会遇到需要修改同事代码的情况。有时可能会花费很长时间却只改动了几行代码,而且改完后还可能引发新的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 中的代码,更好的实践:

  1. 优先使用框架/语言原生解决方案

  2. 遵循最佳实践和设计模式

  3. 进行方案评审和代码审查

  4. 对于第三方库的 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丶


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消