前端代码规范
最近组内经常进行CodeReview,于是参考一些大厂规范以及一些开源的优秀源码,整理了一些前端代码规范,帮助我们后续可以写出更好维护的代码。有什么不对的地方,欢迎大家指出,一起学习进步!
先来看下下面这张思维导图:
下面会就几个方面展开来说。
命名规范
驼峰式命名法介绍
- Pascal Case大驼峰式命名法:首字母大写。eg:PersonInfo
- Camel Case小驼峰式命名法:首字母小写。eg:PersonInfo
文件命名
- 所有文件名统一使用小写,首页命名为index.xxx,文件名禁止特殊字符比如空格、$等。统一使用英文单词或拼音缩写,必须小写。( 为了醒目,某些说明文件的文件名,可以使用大写字母,比如README、LICENSE。 )
- 文件名包含多个单词时,单词之间建议使用半角的连词线 ( - ) 分隔。
- 文件目录结构嵌套层级不要过深
变量命名
命名方式 : 小驼峰式命名方法命名规范 : 类型+对象描述的方式,如果没有明确的类型,就可以使前缀为名词
函数命名
命名方式 : 小驼峰方式 ( 构造函数使用大驼峰命名法 ) 命名规则 : 前缀为动词
例子:
// 是否可跳舞 function canDance(){ return true; } // 获取工作 function getWork{ return this.work }
常量命名
命名方法 : 全部大写命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词。
例子:
const PATH = "xxxx"
类命名
- 公共属性和方法 : 同变量命名方式
- 私有属性和方法 : 前缀为下划线(_或#)后面跟公共属性和方法一样的命名方式
注释规范
单行注释和多行注释的空格保存代码时eslint会帮我们处理,不用手动加空格。
单行注释 ( // )
- 单独一行://(双斜线)与注释文字之间保留一个空格
- 在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。
- 注释代码://(双斜线)与代码之间保留一个空格
多行注释 ( / 注释说明 / )
- 若开始(/
*
和结束(*
/)都在一行,推荐采用单行注释 - 若至少三行注释时,第一行为/
*
,最后行为*
/,其他行以*
开始,并且注释文字与*
保留一个空格。
函数(方法)注释
函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求
/** * 函数说明 * @关键字 */
常用注释关键字
文件目录结构
同功能放在同一个文件目录下,目录结构不要嵌套过深,文件名语义化一些,方便后续维护。
- 文件夹名称全部采用小写+"-" 来隔开;
- 避免多层嵌套,单个项目中的目录嵌套控制在最多三到四个层级内;
例子:
- src 开发目录 - pages 视图 - module-a 模块A - components 私有组件 - ComA.vue - ComB.vue - index.vue - module-b 模块B - components 公共组件 - index.vue 导出所有组件 - header - index.vue - utils 这里是以utils为后缀,JS工具库 - index.js - a.utils.js - b.utils.js - hooks 这里是以hooks为后缀 - index.js - a.hooks.js - b.hooks.js - service api请求,这里是以api为后缀 - a.api.js 按照后端微服务进行划分 - b.api.js - constans 常量
通过对工具函数、hooks、api等加上后缀,更加容易区分引入的文件。
代码规范
JS
JS/TS主流的大致有这几种:
可以参考star最多的进行配置,几乎覆盖了JavaScript的每一项特性。
下面会就代码层面作出阐述。
Vue
遵循vue.js官方风格指南,vuejs.bootcss.com/style-guide…
组件
名称 | 说明 |
---|---|
组件命名 | 1. 组件名为多个单词,命名为组件用途,完整单词的组件名(倾向于完整单词而不是缩写) 2. 文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case),例如:todo-item或TodoItem 3. 基础组件名, 应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如Base、App或V 4. 单例组件名,只应该拥有单个活跃实例的组件应该以The前缀命名,以示其唯一性 5. 紧密耦合的组件名, 和父组件紧密耦合的子组件应该以父组件名作为前缀命名 6. 组件名中的单词顺序, 组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾 7.模板中的组件名大小写,对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是PascalCase的——但是在DOM模板中总是kebab-case的 8. JS/JSX中的组件名大小写,JS/JSX 中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用Vue.component进行全局组件注册时,可以使用kebab-case字符串 |
组件数据 | 组件的data必须是一个函数 |
props定义 | 1. prop定义应该尽量详细 2. prop名大小写,在声明prop的时候,其命名应该始终使用camelCase,而在模板和JSX中应该始终使用kebab-case。 |
v-for使用 | 1. 为v-for设置键值,尽量避免使用index作为key 2. 避免v-if和v-for用在一起 |
样式 | 为组件样式设置作用域,使用scoped属性,使用BEM约定 |
私有property名 | 1. Vue使用前缀来定义其自身的私有 property 2. 推荐使用$ ,作为一个用户定义的私有property的约定,以确保不会和Vue自身相冲突 |
自闭合组件 | 在单文件组件、字符串模板和JSX中没有内容的组件应该是自闭合的——但在DOM模板里永远不要这样做 |
attribute | 1. 多个attribute的元素应该分多行撰写,每个attribute一行 2. 带引号的attribute值 |
模板中简单的表达式 | 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法 |
简单的计算属性 | 应该把复杂计算属性分割为尽可能多的更简单的property,计算属性不能产生副作用 |
指令 | 指令缩写 (用:表示 v-bind:、用@表示 v-on: 和用#表示 v-slot:) |
组件通信 | 应该优先通过prop和事件进行父子组件之间的通信,而不是this.$parent或变更prop |
事件、定时器 | 清除定时器或者事件监听 |
代码文件 | 开发过程中单个文件不允许超过600行,特别复杂的功能,文件不允许超过1000行 |
模板中的组件名大小写
PascalCase相比kebab-case有一些优势:
- 编辑器可以在模板里自动补全组件名,因为PascalCase同样适用于JavaScript
- 视觉上比 更能够和单个单词的HTML元素区别开来,因为前者的不同之处有两个大写字母,后者只有一个横线
- 如果你在模板中使用任何非Vue的自定义元素,比如一个Web Component,PascalCase确保了你的Vue组件在视觉上仍然是易识别的
由于HTML是大小写不敏感的,在DOM模板中必须仍使用kebab-case。
例子:
<!-- 在单文件组件和字符串模板中 --> <MyComponent/> <!-- 在DOM模板中 --> <my-component></my-component> <!-- 在所有地方 --> <my-component></my-component>
Prop名大小写
我们单纯的遵循每个语言的约定。在JavaScript中更自然的是camelCase。而在HTML中则是kebab-case。
文件目录
CSS
CSS检查代码规范
使用stylelint插件,规范则推荐使用stylelint-config-standard
下面简单说下stylelint-config-standard使用
1. 安装 yarn add -D stylelint stylelint-config-standard 2. 在项目的根目录中创建一个配置文件.stylelintrc.json,内容如下: { "extends": "stylelint-config-standard" } 3. 解决与prettier配置的冲突: yarn add -D stylelint-config-prettier 4. 将下面配置复制到.stylelintrc.json中: { "extends": ["stylelint-config-standard", "stylelint-config-prettier"] } 5. 在 git commit 阶段进行检测: "lint-staged": { "**/*": "prettier --write --ignore-unknown", // 格式化 "src/**.{js,jsx,ts,tsx}": "eslint --ext .js,.jsx,.ts,.tsx", // 对js文件检测 "**/*.{less,css}": "stylelint --fix" // 对css文件进行检测 },
BEM命名原则
- block:模块,名字单词间用-连接
- element:元素,模块的子元素,以__与block连接
- modifier:修饰,模块的变体,定义特殊模块,以--与block连接
有效使用css选择器
有效使用css选择器,需遵循以下原则:
- 保持简单,不要使用嵌套过多过于复杂的选择器,选择器嵌套应少于3级;
- 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用;
- 避免使用CSS表达式;
- 慎重选择高消耗的样式(高消耗属性在绘制前需要浏览器进行大量计算),避免重绘重排;
- css选择器中避免使用标签名;
- 尽量使用缩写属性;
- 使用子选择器;
- 0后面不带单位;
- id和class,命名名称语义化,不要过于简单,防止模块之间样式互相影响;合理的使用id,一般情况下id不应该被用于样式,并且id的权重很高,所以不使用id解决样式的问题,而是使用class;
CodeReview常见代码问题汇总
类别 | 描述 | 说明 |
---|---|---|
文件 | 命名 | 1. 组件命名规范,尽量不要和现有组件或远程组件重合,比如页面里使用组件时直接使用Table 2. 文件命名尽量语义化,如果没有定制化的,文件命名不要太定制化,比如文件命名直接是fifth-floor 3. 文件中变量/方法命名语义化,方法名格式统一 |
UI规范 | 删除按钮颜色 | 删除类的操作按钮颜色使用红色 |
vue | 代码问题 | 1. 组件数据共享: 嵌套调用的组件声明一个方法,直接返回当前模块的数据,不要层层嵌套,通过$refs获取,比如:组件里定义一个getValues方法获取数据 2. 生命周期钩子:vue生命周期如果没有依赖关系的话,尽量不要用async/await,可以把请求封装成一个方法,生命周期中直接调用方法;如果有依赖关系的话就用,看场景 3. v-for中key值绑定,尽量不要绑定index,使用id,如果没有id且不涉及添加删除操作时,可以绑定index 4. 使用vue/composition-api时,相同变量以及方法考虑是否放在一起,方便看,看场景及个人习惯 5. 引入第三方工具包时尽量使用小的包,比如moment包换成dayjs 6. 使用动态路由(id) 7. 不建议this传递,问题排查容易出问题,即在其他页面修改this里的变量 |
JS | 代码问题 | 1. map/forEach: 注意区分两者使用场景,不要随便使用 2. 常量:页面里使用多次的字符统一使用常量映射,不要直接在页面中使用字符 3. 代码简洁性:避免使用多次循环列表,如果列表数据很多会有性能问题,比如filter和map嵌套使用 4. 三元表达式: 使用时根据场景可以换成或 5. 否定前置 6. 空数据:接口返回数据为空兼容判断 7. 数组遍历for循环修改为for/of 8. dayjs/moment可以转换一切时间形式为format,不止是时间戳还有标准时间 9. 空值合并运算符(??)使用: 当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数 10. dayjs().format('YYYY-MM-DD HH:mm:ss')无参数时默认取的当前时刻; 有参数时可以考虑把参数提取出来 11. toString()可以写为join(',') 12. 数据为空兼容:用或、?.表示 13. if switch可以考虑转换为json map形式 14. a或b或c改为 [].includes() 15. try/catch捕获错误异常console.dir(error); |
ES6 | 代码问题 | 1. map循环中可以使用解构的话换成解构,避免多层嵌套 2. 解构:能解构尽量解构,边界值兼容处理 |
CSS | 代码问题 | 1. 类名:类名注意不要太简单,直接取name/title/desc之类的,容易和其他人写的类名冲突,比如别人写了同名的类名没有设置scoped或者全局类名,会影响自己的样式 |
map/forEach说明
比如:
# demo1 arr.map(({value = {}, ...item})=>{ return { ...item, ...value }}) # demo2 const type = this.alarmType?.map(item => ({alarmTypeId: item})) || []; # demo3 push行为修改为map this.evidence.printScreen = data.printScreen?.map( ({ id, title, value }) => ({ id, title, value, url: `${imgUrlPre}${id}`, }) );
dayjs.format(str)说明
dayjs.format(str) ,str抽离成下面形式 const DATE_FORMAT_TYPE = { date: 'YYYY-MM-DD', time: 'HH:mm:ss', dateTime: 'YYYY-MM-DD HH:mm:ss', };
相关资料:
- BEM规范:getbem.com/naming/