Code前端首页关于Code前端联系我们

Vue2开发中遵循哪些rules能提升项目质量?

terry 2天前 阅读数 18 #Vue
文章标签 Vue2;开发规范

做前端开发的朋友,只要接触过Vue2,肯定绕不开“代码规范(rules)”这件事,毕竟团队协作时,大家写法五花八门,后期维护像拆盲盒;项目大了性能问题频出,用户体验直线下滑,那Vue2开发里到底要遵循哪些rules,才能让项目质量、可维护性、性能都在线?今天咱们从代码结构、组件设计、状态管理到性能优化,一个个唠明白。

代码结构与文件组织,先把“架子”搭规范

很多人刚写Vue2项目,文件乱放、组件里代码顺序混乱,后期找文件像大海捞针,这部分rules核心是“分层清晰、约定优先”。

项目目录的合理分层

Vue2项目(尤其是SPA)建议按“功能/模块”拆分目录。

  • components放通用组件(按钮、弹窗、表格),views放页面级组件(首页、订单页);
  • utils存工具函数(时间格式化、权限判断),assets放静态资源(图片、全局样式);
  • 有Vuex的话,store下分modules,每个模块对应业务(用户模块user.js、商品模块goods.js),再细分state mutations actions getters

举个实际场景:做电商项目,商品详情页的轮播图组件,该放在components/GoodsCarousel.vue,而商品详情页面组件是views/GoodsDetail.vue,工具函数比如formatPrice.js处理价格格式化,就丢utils里,这样分工明确,下次改轮播图样式,直接去components找;改价格计算逻辑,去utils里定位,效率翻倍。

单文件组件的代码顺序

Vue单文件组件(.vue)里,代码顺序乱会让同事读不懂,官方推荐+社区共识是:

<template> <!-- 模板,放结构 --> </template>  
<script>   <!-- 逻辑,export default里写组件选项 --> </script>  
<style scoped> <!-- 样式,scoped避免污染,按需加lang --> </style>  

而且<script>export default的选项顺序,建议按“组件标识→数据→逻辑→生命周期”排:

export default {  
  name: 'MyComponent', // 组件名,调试和递归组件必须  
  components: {},     // 子组件注册  
  props: {},          // 接收父组件数据  
  data() { return {} },// 响应式数据  
  computed: {},       // 计算属性  
  watch: {},          // 监听器  
  methods: {},        // 方法  
  created() {},       // 生命周期钩子  
  mounted() {}  
}  

这么排的好处是,别人看组件时,先看是啥组件(name)、用了哪些子组件(components)、接收啥参数(props),再看数据和逻辑,逻辑清晰不迷路,比如接手一个“订单详情”组件,先看name: 'OrderDetail',再扫一眼props里的orderId,立刻明白这组件靠orderId渲染订单信息,不用满代码找依赖。

组件设计:“拆得合理,传得明白”是关键

Vue2核心是组件化,组件设计不好,项目会变成“组件堆”——复用难、耦合高,这部分rules围绕“拆分”和“通信”展开。

组件拆分的2个原则

  • 单一职责:一个组件只做一件事,比如登录组件Login.vue,别把注册逻辑也塞进去;搜索框组件SearchInput.vue,只负责输入和触发搜索,搜索结果展示交给SearchResult.vue
  • 可复用性:通用逻辑抽成基础组件,像后台管理系统里的表格BaseTable.vue,封装列配置、分页、筛选,不同页面只要传配置就能用,不用重复写表格结构和逻辑。

反例:曾经接过一个项目,表单组件里又写弹窗、又处理文件上传,几百行代码堆一起,改个输入框样式得翻半天,复用更是不可能,后来拆成FormBase.vue(负责表单结构)、UploadModal.vue(负责文件上传弹窗),每个组件只管自己的事,维护起来爽多了。

props与事件的通信规范

组件通信是Vue2的基础,乱传值、不验证props,后期debug能把人逼疯。

  • props必须验证:别偷懒省略type required default,比如子组件接收父组件的“用户ID”:

    props: {  
    userId: {  
      type: Number, // 明确类型,防止父组件传字符串导致逻辑错误  
      required: true, // 是否必填  
      validator: (value) => value > 0 // 自定义验证,比如ID必须正整数  
    }  
    }  

    要是父组件误传字符串"123",控制台会直接报错,比运行时才发现数据类型不对要友好得多。

  • 事件命名用小写+中划线:子组件触发事件this.$emit('user-login'),父组件<Child @user-login="handleLogin"/>,和HTML原生事件命名一致(比如click input),可读性高,要是写成userLogin,团队里有人习惯驼峰、有人习惯中划线,review时得反复确认,纯纯浪费时间。

  • 双向绑定别滥用v-model:Vue2里v-model本质是value+input事件语法糖,如果是复杂数据双向绑定,建议用“props传值 + $emit更新”,比如父组件传userInfo,子组件this.$emit('update:userInfo', newInfo),父组件用.sync修饰符:<Child :user-info.sync="userInfo"/>,比直接改props(危险,props是单向流)更规范。

响应式数据与状态管理:别让数据“失控”

Vue2的响应式是核心,但用错了(比如直接改数组索引、对象新增属性不触发更新),会出现“数据改了页面没动”的诡异bug,加上Vuex管理全局状态,规则更得严谨。

data、computed、watch的正确打开方式

  • data必须是函数:组件复用(比如循环渲染子组件)时,每个实例要有独立数据,否则数据会互相污染,所以永远写data() { return { msg: 'hi' } },别写成对象,想象一下:循环渲染5个按钮,每个按钮要记录自己的点击次数,如果data是对象,所有按钮会共享同一个count,点任何一个按钮,其他按钮的计数也会变,这显然不对。

  • computed做“派生状态”:有依赖关系、需要缓存的逻辑用computed,比如购物车总价,依赖商品列表和单价,写totalPrice() { return this.goodsList.reduce(...) },比methods里写函数更高效(methods每次调用都执行,computed依赖不变就不执行),要是商品列表没变化,每次渲染组件时,computed不会重复计算,页面渲染速度更快。

  • watch用在“响应式数据变化后的副作用”:比如监听路由变化$route,或者深监听对象/数组,但别滥用,简单逻辑优先用computed,深监听要加deep: true

    watch: {  
    userInfo: {  
      handler(newVal) { /* 处理用户信息变化 */ },  
      deep: true,  
      immediate: true // 初始化时立即执行  
    }  
    }  

    这里deep: true是因为userInfo是对象,直接改里面的name属性,Vue默认监听不到,加了深监听才能触发回调。

Vuex的“铁律”

Vuex是状态管理神器,但用错了会让状态变成“薛定谔的猫”——不知道哪改的。

  • 模块拆分要细:大项目按业务模块(用户、订单、商品)拆分成user.js order.js goods.js,每个模块里state存数据,mutations改数据(只能同步!),actions处理异步(调接口、定时器),getters做派生,比如用户模块管登录态、用户信息;订单模块管订单列表、收货地址,各司其职。

  • mutation必须“专一”:mutation函数名要语义化(比如SET_USER_INFO),只负责修改state,不能放异步逻辑,异步操作全丢action里,action里 commit mutation,举个登录流程:

    // actions.js  
    async login({ commit }, payload) {  
    const res = await api.login(payload)  
    commit('SET_USER_INFO', res.data) // 调用mutation改状态  
    }  

// mutations.js
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
}

为啥mutation不能异步?因为Vuex的devtools要追踪状态变化,异步操作会让“什么时候改的状态”变得模糊,调试时根本找不到状态变化的源头。  
- **状态命名别冲突**:不同模块的state变量,尽量加前缀,比如用户模块`userName`,订单模块`orderName`,避免getters或mapState时混淆,要是都叫`name`,用`mapState`导入时,根本分不清是用户名称还是订单名称。  
### 四、性能优化:这些rules让页面“跑”得更快  
Vue2项目功能做出来不难,难的是用户操作时不卡顿,性能优化的rules,核心是“减少不必要的渲染和计算”。  
#### 1. 列表渲染必须加key  
用`v-for`循环时,`key`必须绑定唯一值(item.id`),别用索引(索引会因为数据增删导致key变化,触发不必要的DOM更新)。  
```vue
<template>  
  <ul>  
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>  
  </ul>  
</template>  

Vue的diff算法靠key来识别节点,如果没key,Vue默认用“就地复用”策略,数据变化时可能出现DOM节点和数据不匹配(比如输入框内容错位),加key能让Vue精准更新DOM,避免无效渲染。

避免不必要的响应式

Vue的响应式是通过Object.defineProperty实现的,但有些数据不需要响应式(比如静态配置、接口返回的大列表),可以用Object.freeze冻结,减少性能开销。

data() {  
  return {  
    staticConfig: Object.freeze({ theme: 'dark', size: 'medium' }),  
    // 接口返回的1000条商品数据,不需要响应式更新,冻结后Vue不会递归劫持  
    goodsList: Object.freeze(res.data)  
  }  
}  

函数式组件(<template functional>)适合纯展示、无状态的组件,因为没有响应式开销,渲染更快,比如导航栏里的菜单项,纯展示且不需要自己的状态,用函数式组件能省不少性能。

异步加载与资源懒加载

  • 路由懒加载:Vue Router配置时,用() => import('./views/GoodsDetail.vue'),让页面按需加载,首屏加载更快,想象一下,用户打开首页时,不需要加载订单页、商品详情页的代码,等用户点击对应路由时再加载,首屏加载时间直接减半。
  • 异步组件:组件很大(比如可视化编辑器),用const BigEditor = () => import('./components/BigEditor.vue'),在需要时再加载,比如后台管理系统的“报表生成器”组件,只有管理员点击时才加载,普通用户根本用不到,没必要首屏加载。

事件与定时器要及时销毁

组件销毁时,没清理定时器、自定义事件,会导致内存泄漏。

mounted() {  
  this.timer = setInterval(() => { /* 轮询接口 */ }, 5000)  
  this.$bus.$on('custom-event', this.handleEvent) // 自定义事件  
},  
beforeDestroy() {  
  clearInterval(this.timer)  
  this.$bus.$off('custom-event', this.handleEvent)  
}  

要是不销毁定时器,组件销毁后定时器还在跑,不仅浪费性能,还可能触发已销毁组件的方法,导致报错。

代码风格与协作:让团队“读得懂,改得顺”

多人协作的Vue2项目,代码风格不统一,review时像看“火星文”,这部分rules靠工具(ESLint)+ 约定来约束。

ESLint + Prettier 打造“统一风格”

Vue官方有eslint-plugin-vue,能检测组件语法错误、代码风格,结合Prettier格式化代码,团队里每个人提交代码前,自动格式化+ lint校验,配置文件里可以规定:

  • 组件名用PascalCase(比如UserProfile.vue),模板里引用用kebab-case(<user-profile/>);
  • props命名用camelCase(比如userName),模板里传值用kebab-case(<child :user-name="name"/>);
  • 方法名语义化(handleLogin而不是fn1),避免魔法数字(用常量代替,比如const PAGE_SIZE = 10)。

比如团队约定“组件名必须PascalCase”,新人写了user-info.vue,ESLint会直接报错,强制统一风格。

注释规范:给代码“贴说明书”

  • 组件级注释:在<script>顶部用多行注释,说明组件功能、props含义、事件触发时机。
    /**  
  • 商品列表组件
  • @props {Array} goods - 商品数据列表,每个项包含name/price/id
  • @props {Number} page - 当前页码
  • @emits {Function} page-change - 页码变化时触发,参数是新页码
    /
    export default {
    name: 'GoodsList',
    props: { /
    ... */ }
    }
    
    新人接手项目时,看这段注释就知道组件是干啥的、怎么传值、怎么监听事件,不用到处翻代码。  
  • 复杂逻辑注释:methods里的复杂函数、watch里的深监听逻辑,加注释说明“做了什么,为什么这么做”,比如处理购物车全选的逻辑:
    methods: {  
    handleCheckAll() {  
      // 全选时,遍历所有商品,同步选中状态到每个商品的checked属性  
      // 注意:需要触发子组件的状态更新,所以用$set逐个修改  
      this.goodsList.forEach((item, index) => {  
        this.$set(this.goodsList, index, { ...item, checked: this.isAllChecked })  
      })  
    }  
    }  

    要是没注释,别人看这段代码可能疑惑“为啥不用直接赋值,非要用$set?”,有了注释就明白是为了触发Vue的响应式更新。

看完这些Vue2的rules,是不是感觉“原来规范不是束缚,而是让项目更丝滑的密码”?从代码结构到组件设计,从状态管理到性能优化,再到团队协作,每一条规则都是前人踩过的坑、攒下的经验,实际项目里,把这些rules落地(比如结合Git Hooks做提交前校验、写组件时先想拆分逻辑),项目的可维护性、性能、协作效率都会上一个台阶,毕竟,好的代码规范,不是为了应付领导,而是让自己和同事少加班、少掉头发呀~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门