前言
该文章旨在增强团队开发协作、提高代码质量和打造开发基石的编码规范。
一、Vue风格指南
组件名为多个单词
- 可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的 详情
// bad
app.component('todo', {
// ...
})
// good
app.component('todo-item', {
// ...
})
Prop 定义应尽量详细
- prop 的定义应该尽量详细,至少需要指定其类型 详情
// bad
props: ['status']
// good
props: {
status: String
}
// better
props: {
status: {
type: String,
required: true,
validator: value => {
return [
'syncing',
'synced',
'version-conflict',
'error'
].includes(value)
}
}
}
为 v-for 设置 key 值
- 在组件上总是必须用
key
配合v-for
,以便维护内部组件及其子树的状态 详情
<!-- bad -->
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
<!-- good -->
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
避免 v-if 和 v-for 一起使用
- 永远不要把
v-if
和v-for
同时用在同一个元素上 详情
<!-- bad -->
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<!-- good -->
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
为组件样式设置作用域
- 设置一致的作用域会确保样式只会运用在想要作用的组件上 详情
<!-- bad -->
<style>
.btn-close {
background-color: red;
}
</style>
<!-- good -->
<!-- 使用 `scoped` attribute -->
<style scoped>
.button {
border: none;
border-radius: 2px;
}
</style>
模板中的简单表达式
- 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法 详情
<!-- bad -->
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
// good
// 复杂表达式已经移入一个计算属性
computed: {
normalizedFullName() {
return this.fullName.split(' ')
.map(word => word[0].toUpperCase() + word.slice(1))
.join(' ')
}
}
指令缩写
- 指令缩写 (用
:
表示v-bind:
,@
表示v-on:
和用#
表示v-slot
) 应该要么都用要么都不用 详情
<!-- bad -->
<input
v-bind:value="newTodoText"
@focus="onFocus"
>
<!-- good -->
<input
:value="newTodoText"
@focus="onFocus"
>
组件/实例的选项顺序
- 组件/实例的选项应该有统一的顺序 详情
-
全局感知 (要求组件以外的知识)
name
-
模板依赖 (模板内使用的资源)
components
directives
-
组合 (向选项里合并 property)
extends
mixins
provide / inject
-
接口 (组件的接口)
inheritAttrs
props
emits
-
组合式 API (使用组合式 API 的入口点)
setup
-
本地状态 (本地的响应式 property)
data
computed
-
事件 (通过响应式事件触发的回调)
watch
生命周期钩子 (按照它们被调用的顺序)
beforeCreate
created
beforeMount
mounted
beforeUpdate
activated
deactivated
beforeUnmount
unmounted
errorCaptured
renderTracked
renderTriggered
-
非响应式的 property (不依赖响应性系统的实例 property)
methods
-
渲染 (组件输出的声明式描述)
template / render
更多内容
二、JS规范
1. 引用
- 请记得
const
和let
都是块级作用域,var
是函数级作用域;{ let a = 1 const b = 1 } console.log(a) // ReferenceError console.log(b) // ReferenceError
- 对所有引用都使用
const
,不要使用var
;// bad var a = 1 var b = 2 // good const a = 1 const b = 2
- 如果引用是可变动的,使用
let
代替var
。// bad var count = 1 if (count < 10) { count += 1 } // good let count = 1 if (count < 10) { count += 1 }
2. 对象
-
请使用字面量值创建对象;
// bad const a = new Object{} // good const a = {}
-
当使用动态属性名创建对象时,请使用对象计算属性名来进行创建;
function getKey(k) { return `a key named ${k}` } // bad const obj = { id: 5, name: 'San Francisco' }; obj[getKey('enabled')] = true // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true };
-
请使用对象方法和属性的简写方式;
// bad const item = { value: 1, addValue: function (val) { return item.value + val } } // good const item = { value: 1, addValue (val) { return item.value + val } }
-
请使用对象属性值的简写方式;
const job = 'FrontEnd' // bad const item = { job: job } // good const item = { job }
-
将简写的对象属性分组后统一放到对象声明的开头;
const job = 'FrontEnd' const department = 'JDC' // bad const item = { sex: 'male', job, age: 25, department } // good const item = { job, department, sex: 'male', age: 25 }
-
只对非法标识符的属性使用引号;
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5 } // good const good = { foo: 3, bar: 4, 'data-blah': 5 }
-
优先使用对象展开运算符
...
来做对象浅拷贝而不是使用Object.assign
,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象。// very bad const original = { a: 1, b: 2 } const copy = Object.assign(original, { c: 3 }) // this mutates `original` ಠ_ಠ delete copy.a // so does this // bad const original = { a: 1, b: 2 } const copy = Object.assign({}, original, { c: 3 }) // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 } const copy = { ...original, c: 3 } // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy // noA => { b: 2, c: 3 }
3. 数组
- 请使用字面量值创建数组
// bad const items = new Array() // good const items = []
- 向数组中添加元素时,请使用
push
方法const items = [] // bad items[items.length] = 'test' // good items.push('test')
- 使用展开运算符 … 复制数组
// bad const items = [] const itemsCopy = [] const len = items.length let i // bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i] } // good itemsCopy = [...items]
- 使用数组的
map
等方法时,请使用return
声明,如果是单一声明语句的情况,可省略return
// good [1, 2, 3].map(x => { const y = x + 1 return x * y }) // good [1, 2, 3].map(x => x + 1) // bad const flat = {} [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item) flat[index] = flatten }) // good const flat = {} [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item) flat[index] = flatten return flatten })
4. 解构赋值
- 当需要使用对象的多个属性时,请使用解构赋值
// bad function getFullName (user) { const firstName = user.firstName const lastName = user.lastName return `${firstName} ${lastName}` } // good function getFullName (user) { const { firstName, lastName } = user return `${firstName} ${lastName}` } // better function getFullName ({ firstName, lastName }) { return `${firstName} ${lastName}` }
- 当需要使用数组的多个值时,请同样使用解构赋值
const arr = [1, 2, 3, 4] // bad const first = arr[0] const second = arr[1] // good const [first, second] = arr
- 函数需要回传多个值时,请使用对象的解构,而不是数组的解构
// bad function doSomething () { return [top, right, bottom, left] } // 如果是数组解构,那么在调用时就需要考虑数据的顺序 const [top, xx, xxx, left] = doSomething() // good function doSomething () { return { top, right, bottom, left } } // 此时不需要考虑数据的顺序 const { top, left } = doSomething()
5. 字符串
- 字符串统一使用单引号的形式
'
// bad const department = "JDC" // good const department = 'JDC'
- 字符串太长的时候,请不要使用字符串连接符换行
\
,而是使用+
const str = '前端代码规范 前端代码规范 前端代码规范' + '前端代码规范 前端代码规范 前端代码规范' + '前端代码规范 前端代码规范'
- 程序化生成字符串时,请使用模板字符串
const test = 'test' // bad const str = ['a', 'b', test].join() // bad const str = 'a' + 'b' + test // good const str = `ab${test}`
6. 函数
- 不要使用
arguments
,使用 剩余运算符...
// bad function test () { const args = Array.prototype.slice.call(arguments) return args.join('') } // good function test (...args) { return args.join('') }
- 使用参数默认值语法而不是修改函数参数
// really bad function handleThings (opts) { // No! We shouldn't mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {} // ... } // still bad function handleThings (opts) { if (opts === void 0) { opts = {} } // ... } // good function handleThings (opts = { }) { // ... }
- 将参数默认值放在最后
// bad function handleThings (opts = {}, name) { // ... } // good function handleThings (name, opts = {}) { // ... }
- 不要更改参数或重新赋值
// bad function f1 (a) { a = 1 } function f2 (a) { if (!a) { a = 1 } } // good function f3 (a) { const b = a || 1 } function f4 (a = 1) { }
7. 模块
- 使用标准的 ES6 模块语法
import
和export
// bad const util = require('./util') module.exports = util // good import Util from './util' export default Util // better import { Util } from './util' export default Util
- 同个文件每个模块只允许
import
一次,有多个import
请书写在一起// bad import foo from 'foo' // … some other imports … // import { named1, named2 } from 'foo' // good import foo, { named1, named2 } from 'foo' // good import foo, { named1, named2 } from 'foo'
- 将所有
import
语句放在文件最前方// bad import foo from 'foo' foo.init() import bar from 'bar' // good import foo from 'foo' import bar from 'bar' foo.init()
- 多行导入应该像多行数组和对象文字一样缩进
// bad import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path' // good import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path'
8. 对象属性
- 使用
.
来访问对象属性const joke = { name: 'haha', age: 28 } // bad const name = joke['name'] // good const name = joke.name
- 当访问的属性是变量时使用
[]
const luke = { jedi: true, age: 28, } function getProp (prop) { return luke[prop] } const isJedi = getProp('jedi')
9. 变量声明
- 声明变量时,请使用
const
、let
关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么// bad demo = new Demo() // good const demo = new Demo()
- 将所有的
const
和let
分组// bad let a const b let c const d let e // good const b const d let a let c let e
10. 比较运算符&相等
- 使用
===
和!==
而非==
和!=
更多内容
三. css规范
1. 属性书写顺序
- 布局定位属性:display / position / float / clear / visibility / overflow
- 自身属性:width / height / margin / padding / border / background
- 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
- 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient …
.jdc { display: block; position: relative; float: left; width: 100px; height: 100px; margin: 0 10px; padding: 20px 0; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; color: #333; background: rgba(0,0,0,.5); -webkit-border-radius: 10px; -moz-border-radius: 10px; -o-border-radius: 10px; -ms-border-radius: 10px; border-radius: 10px; }
2. 代码大小写
- 样式选择器,属性名,属性值关键字全部使用小写字母书写
/* 推荐 */ .jdc{ display:block; } /* 不推荐 */ .JDC{ DISPLAY:BLOCK; }
3. 选择器
- 尽量少用通用选择器
*
- 不使用 ID 选择器
- 不使用无具体语义定义的标签选择器
/* 推荐 */ .jdc {} .jdc li {} .jdc li p{} /* 不推荐 */ *{} #jdc {} .jdc div{}
4. 代码易读性
- 属性值十六进制数值能用简写的尽量用简写
/* 推荐 */ .jdc { color: #fff; } /* 不推荐 */ .jdc { color: #ffffff; }
- 不要为 0 指明单位
/* 推荐 */ .jdc { margin: 0 10px; } /* 不推荐 */ .jdc { margin: 0px 10px; }
5. 变量
- 可复用属性尽量抽离为页面变量,易于统一维护
// CSS .jdc { color: red; border-color: red; } // SCSS $color: red; .jdc { color: $color; border-color: $color; }
更多内容
四. HTML规范
1. HTML代码大小写
- HTML标签名、类名、标签属性和大部分属性值统一用小写
<!-- 推荐 --> <input type="text"> <input type="radio" name="name" checked="checked" > <!-- 不推荐 --> <input type=text> <input type='text'> <input type="radio" name="name" checked >
2. 元素属性
- 元素属性值使用双引号语法
- 元素属性值可以写上的都写上
<!-- 推荐 --> <div class="demo"></div> <!-- 不推荐 --> <div class="DEMO"></div> <DIV CLASS="DEMO"></DIV>
3. 纯数字输入框
- 使用
type="tel"
而不是type="number"
<input type="tel">
4. 代码嵌套
- 元素嵌套规范,每个块状元素独立一行,内联元素可选
<!-- 推荐 --> <div> <h1></h1> <p></p> </div> <p><span></span><span></span></p> <!-- 不推荐 --> <div> <h1></h1><p></p> </div> <p> <span></span> <span></span> </p>
- 段落元素与标题元素只能嵌套内联元素
<!-- 推荐 --> <h1><span></span></h1> <p><span></span><span></span></p> <!-- 不推荐 --> <h1><div></div></h1> <p><div></div><div></div></p>
更多内容
五. 代码Commite规范
格式
Commit 规范采用常用的 Angular 团队所使用的规范,具体如下:
<type><scope>: <subject>
<空行>
<body>
<空行>
<footer>
type 规则(必填)
type 代表本次 commit 的类型,有且仅有如下几种:
- feat - 功能性更新
- fix - bug 修复
- style - 改变代码格式(如删除空行、格式化代码、去除不必要的分号等等)
- refactor - 既不是功能更新也不是 bug 修复的更改(建议对代码进行重构的时候使用)
- perf - 代码改变提高了性能
- test - 添加测试用例或者修改测试用例
- build - 由打包工具造成的改变(如gulp、webpack编译文件)
- chore - 既不是源码的修改,也不是测试用例的修改(修改项目相关配置时可以使用)
- revert - 撤销之前的提交
scope 规则(必填)
scope 代表本次 commit 的影响范围,暂定规则如下:
- 本次 commit 修改的组件
- 本次 commit 修改的文件
- 本次 commit 修改的文件夹
subject 规则(必填)
用一句简短的话描述本次修改的内容,不要超过30个汉字,以动词开头
建议选用如下动词:
- 新增(组件、属性、事件、API)
- 删除
- 修正
- 修复
- 修改
body 规则(选填)
如果 subject 无法对本次 commit 进行清楚的阐释,则在 body 中进行补充说明。
建议填写以下内容:
- 为什么进行本次修改
- 本次修改了哪些内容
- 修改后的影响有哪些
footer 规则(选填)
footer 中只填写两种内容:
- 这次 commit 和某个 issue 相关联,提交后能关闭该 issue,则填写:
close #748
- 这次 commit 有不兼容上个版本的代码,则以BREAKING CHANGE:开头填写不兼容信息,如下:
BREAKING CHANGE: Message组件top属性单位由px改为rpx
示例
一个完整规范且正确的 Commit 示例如下:
fix(NoticeBar):修改top属性单位为rpx
NoticeBar组件的top属性单位之前为px,会出现无法自适应的问题。
更改为rpx后可对屏幕进行自适应。
BREAKING CHANGE: Notice-Bar组件top属性单位由px改为rpx
Close #745
错误示例
subject
描述中出现组件名称
因type(括号中的内容)已经指定了组件,所以 subject 描述信息中无需再指明组件feat(Button): Button 组件新增 size 属性
- 单词未添加空格
为了生成的 changelog 的可读性,所有的单词左右都需要添加一个空格feat(Button): 新增size属性
其他事项
一个 commit 应该是一个有意义的 commit
有意义的定义如下:
- 新增了一个功能或组件
- 修复了一个bug
- 解决了一个issue
- 重构了某个组件或文件
- 改善了现有代码的构建流程或风格
无意义的定义如下:
- 临时工作进度保存
- 误提交的 commit
- commit 信息不规范或缺失
- subject 无法准确描述此次 commit