编码规范
# 简介
当前规范,目前只是参考网上主流规范,待确认后使用
# 代码静态检查工具
# eslint
eslint
检查的规范继承自eslint-config-standard
检验规则,具体的规则介绍参照链接:cn.eslint.org/docs/rules/ (opens new window) ,这里及以下部分不再重复介绍这些检验规则。
# stylelint
stylelint
检查的规范继承自 stylelint-config-standard
检验规则,具体的规则介绍参照链接:www.npmjs.com/package/sty… (opens new window) ,这里及以下部分不再重复介绍这些检验规则。
# 命名规范
# JS 采用 Camel Case 小驼峰式命名
推荐:
studentInfot
# 避免名称冗余
推荐:
const Car = {
make: "Honda",
model: "Accord",
color: "Blue"
};
2
3
4
5
不推荐:
const Car = {
carMake: "Honda",
carModel: "Accord",
carColor: "Blue"
};
2
3
4
5
# CSS 类名采用 BEM 命名规范
推荐:
.block__element{}
.block--modifier{}
2
# 命名符合语义化
命名需要符合语义化,如果函数命名,可以采用加上动词前缀:
动词 | 含义 |
---|---|
can | 判断是否可执行某个动作 |
has | 判断是否含有某个值 |
is | 判断是否为某个值 |
get | 获取某个值 |
set | 设置某个值 |
推荐:
//是否可阅读
function canRead(){
return true;
}
//获取姓名
function getName{
return this.name
}
2
3
4
5
6
7
8
# HTML 篇
# 启用标准模式
使用 HTML5 的 doctype
来启用标准模式
<!DOCTYPE html>
# 统一使用 UTF-8 编码
<meta charset="utf-8" />
# 优先使用 IE 最新版本和 Chrome
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
# 移动设备添加 viewport
<meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no" />
# 自闭合标签无需闭合
例如: img
, input
, br
, hr
等
<img src="https://xxx.png" alt="Google" />
<br />
<input type="text" name="title" />
2
3
# 使用语义化标签
html 的标签能使用语义化的,尽量使用语义化标签,避免一个页面都是 div 或者 p 标签
<!-- bad -->
<div>
<p></p>
</div>
<!-- good -->
<header></header>
<footer></footer>
2
3
4
5
6
7
8
# 属性顺序要求
HTML 属性应该按照特定的顺序出现以保证易读性。
id
class
name
data-xxx
src, for, type, href
title, alt
aria-xxx, role
2
3
4
5
6
7
# CSS 篇
# BEM 命名原则
- block:模块,名字单词间用 - 连接
- element:元素,模块的子元素,以 __ 与 block 连接
- modifier:修饰,模块的变体,定义特殊模块,以 -- 与 block 连接
/* 举个例子 */
.block__element {
}
.block--modifier {
}
2
3
4
5
6
# 有效使用 css 选择器
选择器嵌套应少于 3 级
/* bad */
.page .header .login #username input {
}
/* good */
#username input {
}
2
3
4
5
6
7
有效使用 css 选择器,因遵循以下原则
- 保持简单,不要使用嵌套过多过于复杂的选择器。
- 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
- 不要使用类选择器和 ID 选择器修饰元素标签。
- 不要为了追求速度而放弃可读性与可维护性
- 避免使用 CSS 表达式
# 慎重选择高消耗的样式
高消耗属性在绘制前需要浏览器进行大量计算:
box-shadows
border-radius
transparency
transforms
CSS filters(性能杀手)
2
3
4
5
# 避免重绘重排
当发生重排的时候,浏览器需要重新计算布局位置与大小,不利于性能优化。
常见引起重绘重排属性和方法
- 添加或者删除可见的
DOM
元素; - 元素尺寸改变——边距、填充、边框、宽度和高度
- 内容变化,比如用户在
input
框中输入文字 - 浏览器窗口尺寸改变——
resize
事件发生时 - 计算
offsetWidth
和offsetHeight
属性 - 设置
style
属性的值
减少重绘重排的方法
- 使用
transform
替代top
- 使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局) - 不要把节点的属性值放在一个循环里当成循环里的变量。
- 不要使用
table
布局,可能很小的一个小改动会造成整个table
的重新布局 - 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame
- CSS 选择符从右往左匹配查找,避免节点层级过多
# Javascript 篇
# 组合规定
# 关于命名
普通命名采用小驼峰式命名
let userName = 'jack'
命名是复数的时候需要加 s,比如说我想声明一个数组,表示很多人的名字
let names = new Array()
每个常量都需命名,这样更利于别人读懂含义
// good
const COL_NUM = 10
let row = Math.ceil(num / COL_NUM)
// bad
let row = Math.ceil(num / 10)
2
3
4
5
6
命名需要符合语义化,如果函数命名,可以采用加上动词前缀:
- can 判断是否可执行某个动作
- has 判断是否含有某个值
- is 判断是否为某个值
- get 获取某个值
- set 设置某个值
//是否可阅读
function canRead(){
return true;
}
//获取姓名
function getName{
return this.name
}
2
3
4
5
6
7
8
# 关于字符串
统一使用单引号而不是双引号
// bad
const name = 'samy'
// good
const name = 'samy'
2
3
4
5
用字符串模板而不是 '+' 来拼接字符串
function sayHi(name) {
return 'How are you, ' + name + '?'
}
// good
function sayHi(name) {
return `How are you, ${name}?`
}
2
3
4
5
6
7
8
# 关于数组
用字面量赋值
// bad
const items = new Array()
// good
const items = []
2
3
4
5
用扩展运算符做数组浅拷贝
// bad
let arr = [1, 2, 3]
const len = arr.length
const copyArr = []
for (let i = 0; i < len; i += 1) {
copyArr[i] = arr[i]
}
// good
const copyArr = [...arr]
2
3
4
5
6
7
8
9
10
11
用 Array.from 去将一个类数组对象转成一个数组。
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }
// bad
const arr = Array.prototype.slice.call(arrLike)
// good
const arr = Array.from(arrLike)
2
3
4
5
6
7
使用数组解构
const arr = [1, 2, 3, 4]
// bad
const first = arr[0]
const second = arr[1]
// good
const [first, second] = arr
2
3
4
5
6
7
8
# 关于对象
创建对象和数组推荐使用字面量,因为这不仅是性能最优也有助于节省代码量。
// good
let obj = {
name: 'samy',
age: 15,
sex: '男',
}
// bad
let obj = {}
obj.name = 'samy'
obj.age = 15
obj.sex = '男'
2
3
4
5
6
7
8
9
10
11
12
ES6 使用属性值缩写
const lukeSkywalker = 'Luke Skywalker'
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
}
// good
const obj = {
lukeSkywalker,
}
2
3
4
5
6
7
8
9
10
11
将属性的缩写放在对象声明的开头
const anakinSkywalker = 'Anakin Skywalker'
const lukeSkywalker = 'Luke Skywalker'
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
}
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
对象浅拷贝时,更推荐使用扩展运算符 ...,而不是 Object.assign。解构赋值获取对象指定的几个属性时,推荐用 rest 运算符,也是 ...。
// very bad
const original = { a: 1, b: 2 }
const copy = Object.assign(original, { c: 3 })
delete copy.a // 改变了 original
// 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 }
2
3
4
5
6
7
8
9
10
11
12
13
14
# 关于函数
函数参数使用默认值替代使用条件语句进行赋值。
// good
function createMicrobrewery(name = 'Jack') {
...
}
// bad
function createMicrobrewery(name) {
const userNameName = name || 'Jack'
...
}
2
3
4
5
6
7
8
9
10
函数参数使用结构语法,函数参数越少越好,如果参数超过两个,要使用 ES6 的解构语法,不用考虑参数的顺序。
// good
function createMenu({ title, body, buttonText, cancellable }) {
...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true,
})
// bad
function createMenu(title, body, buttonText, cancellable) {
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
优先使用 rest 语法...,而不是 arguments
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments)
return args.join('')
}
// good
function concatenateAll(...args) {
return args.join('')
}
2
3
4
5
6
7
8
9
10
把默认参数赋值放在最后
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
2
3
4
5
6
7
8
9
尽量使用箭头函数
// bad
[1, 2, 3].map(function (x) {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map((x) => {
const y = x + 1
return x * y
})
2
3
4
5
6
7
8
9
10
11
12
# 关于模块
在非标准模块系统上使用(import/export)
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide')
module.exports = AirbnbStyleGuide.es6
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide'
export default AirbnbStyleGuide.es6
// best
import { es6 } from './AirbnbStyleGuide'
export default es6
2
3
4
5
6
7
8
9
10
11
12
一个入口只 import 一次
// bad
import foo from 'foo'
// … some other imports … //
import { named1, named2 } from 'foo'
// good
import foo, { named1, named2 } from 'foo'
2
3
4
5
6
7
8
在只有一个导出的模块里,用 export default 更好
// bad
export function foo() {}
// good
export default function foo() {}
2
3
4
5
# for 循环
使用 for 循环过程中,数组的长度,使用一个变量来接收,这样有利于代码执行效率得到提高,而不是每走一次循环,都得重新计算数组长度
// bad
for(var i = 0; i < arr.length; i++){
}
// good
for(var i = 0; len = arr.length; i < len; i++){
}
2
3
4
5
6
7
8
9
# 每个常量都需命名
每个常量应该命名,不然看代码的人不知道这个常量表示什么意思。
推荐:
const COL_NUM = 10;
let row = Math.ceil(num/COL_NUM);
2
不推荐:
let row = Math.ceil(num/10);
# 推荐使用字面量
创建对象和数组推荐使用字面量,因为这不仅是性能最优也有助于节省代码量。
推荐:
let obj = {
name:'tom',
age:15,
sex:'男'
}
2
3
4
5
不推荐:
let obj = {};
obj.name = 'tom';
obj.age = 15;
obj.sex = '男';
2
3
4
# 对象设置默认属性的推荐写法
推荐:
const menuConfig = {
title: "Order",
// User did not include 'body' key
buttonText: "Send",
cancellable: true
};
function createMenu(config) {
config = Object.assign(
{
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
},
config
);
// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
不推荐:
const menuConfig = {
title: null,
body: "Bar",
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || "Foo";
config.body = config.body || "Bar";
config.buttonText = config.buttonText || "Baz";
config.cancellable =
config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 将对象的属性值保存为局部变量
对象成员嵌套越深,读取速度也就越慢。所以好的经验法则是:如果在函数中需要多次读取一个对象属性,最佳做法是将该属性值保存在局部变量中,避免多次查找带来的性能开销。
推荐:
let person = {
info:{
sex:'男'
}
}
function getMaleSex(){
let sex = person.info.sex;
if(sex === '男'){
console.log(sex)
}
}
2
3
4
5
6
7
8
9
10
11
不推荐:
let person = {
info:{
sex:'男'
}
}
function getMaleSex(){
if(person.info.sex === '男'){
console.log(person.info.sex)
}
}
2
3
4
5
6
7
8
9
10
11
# 字符串转为整型
当需要将浮点数转换成整型时,应该使用Math.floor()
或者Math.round()
,而不是使用parseInt()
将字符串转换成数字。Math 是内部对象,所以``Math.floor()
其实并没有多少查询方法和调用时间,速度是最快的。
推荐:
let num = Math.floor('1.6');
不推荐:
let num = parseInt('1.6');
# 函数参数
函数参数越少越好,如果参数超过两个,要使用 ES6
的解构语法,不用考虑参数的顺序。
推荐:
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
2
3
4
5
6
7
8
9
10
11
不推荐:
function createMenu(title, body, buttonText, cancellable) {
// ...
}
2
3
4
# 使用参数默认值
使用参数默认值 替代 使用条件语句进行赋值。
推荐:
function createMicrobrewery(name = "Hipster Brew Co.") {
// ...
}
2
3
4
不推荐:
function createMicrobrewery(name) {
const breweryName = name || "Hipster Brew Co.";
// ...
}
2
3
4
# 最小函数准则
这是一条在软件工程领域流传久远的规则。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用 。
# 3.9、不要写全局方法
在 JavaScript
中,永远不要污染全局,会在生产环境中产生难以预料的 bug
。举个例子,比如你在 Array.prototype
上新增一个 diff
方法来判断两个数组的不同。而你同事也打算做类似的事情,不过他的 diff
方法是用来判断两个数组首位元素的不同。很明显你们方法会产生冲突,遇到这类问题我们可以用 ES2015/ES6
的语法来对 Array
进行扩展。
推荐:
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
2
3
4
5
6
不推荐:
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
2
3
4
5
6
# 推荐函数式编程
函数式变编程可以让代码的逻辑更清晰更优雅,方便测试。
推荐:
const programmerOutput = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
let totalOutput = programmerOutput
.map(output => output.linesOfCode)
.reduce((totalLines, lines) => totalLines + lines, 0)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
不推荐:
const programmerOutput = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用多态替换条件语句
为了让代码更简洁易读,如果你的函数中出现了条件判断,那么说明你的函数不止干了一件事情,违反了函数单一原则 ;并且绝大数场景可以使用多态替代
推荐:
class Airplane {
// ...
}
// 波音777
class Boeing777 extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
// 空军一号
class AirForceOne extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
// 赛纳斯飞机
class Cessna extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
不推荐:
class Airplane {
// ...
// 获取巡航高度
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 定时器是否清除
代码中使用了定时器 setTimeout
和 setInterval
,需要在不使用时进行清除。
# SCSS 推荐写法
# 变量 $ 使用
利用scss
中的变量配置,可以进行项目的颜色、字体大小统一更改(换肤),有利于后期项目的维护。
推荐:
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;
2
3
4
5
# @import 导入样式文件
scss
中的@import
规则在生成css
文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css
文件中,而无需发起额外的下载请求,在构建我们自己的组件库时推荐使用。
@import "./base.scss";
@import "./pagination.scss";
@import "./dialog.scss";
@import "./autocomplete.scss";
@import "./dropdown.scss";
@import "./dropdown-menu.scss";
2
3
4
5
6
7
8
# 局部文件命名的使用
scss
局部文件的文件名以下划线开头。这样,scss
就不会在编译时单独编译这个文件输出css
,而只把这个文件用作导入。
推荐:
# 父选择器标识符 & 实现BEM 命令规范
scss
的嵌套和父选择器标识符 & 能解决BEM
命名的冗长,且使样式可读性更高。
推荐:
.el-input {
display: block;
&__inner {
text-align: center;
}
}
2
3
4
5
6
# @mixin 混合器的使用
mixin
混合器用来实现大段样式的重用,减少代码的冗余,且支持传参。
@mixin button-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) {
padding: $padding-vertical $padding-horizontal;
font-size: $font-size;
border-radius: $border-radius;
&.is-round {
padding: $padding-vertical $padding-horizontal;
}
}
@include m(medium) {
@include button-size($--button-medium-padding-vertical, $--button-medium-padding-horizontal, $--button-medium-font-size, $--button-medium-border-radius);
}
@include m(small) {
@include button-size($--button-small-padding-vertical, $--button-small-padding-horizontal, $--button-small-font-size, $--button-small-border-radius);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# @extend 指令的使用
(1)使用@extend
产生 DRY CSS (opens new window)风格的代码(Don't repeat yourself)
(2)@mixin
主要的优势就是它能够接受参数。如果想传递参数,你会很自然地选择@mixin
而不是@extend
推荐:
.common-mod {
height: 250px;
width: 50%;
background-color: #fff;
text-align: center;
}
.show-mod--right {
@extend .common-mod;
float: right;
}
.show-mod--left {
@extend .common-mod;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# #{} 插值的使用
插值能动态定义类名的名称,当有两个页面的样式类似时,我们会将类似的样式抽取成页面混合器,但两个不同的页面样式的命名名称根据BEM
命名规范不能一样,这时我们可使用插值进行动态命名。
推荐:
@mixin home-content($class) {
.#{$class} {
position: relative;
background-color: #fff;
overflow-x: hidden;
overflow-y: hidden;
&--left {
margin-left: 160px;
}
&--noleft {
margin-left: 0;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# each遍历、map数据类型、@mixin/@include混合器、#{}插值 结合使用
可通过each
遍历、map
数据类型、@mixin/@include
混合器、#{} 插值 结合使用,从而减少冗余代码,使代码更精简。
推荐:
$img-list: (
(xlsimg, $papers-excel),
(xlsximg, $papers-excel),
(gifimg, $papers-gif),
(jpgimg, $papers-jpg),
(mp3img, $papers-mp3),
(mp4img, $papers-mp3),
(docimg, $papers-word),
(docximg, $papers-word),
(rarimg, $papers-zip),
(zipimg, $papers-zip),
(unknownimg, $papers-unknown)
);
@each $label, $value in $img-list {
.com-hwicon__#{$label} {
@include commonImg($value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# scss 自带函数的应用
scss
自带函数的应用,从而进行相关的计算,例如 mix
函数的使用如下。
@include m(text) {
&:hover,
&:focus {
color: mix($--color-white, $--color-primary, $--button-hover-tint-percent);
border-color: transparent;
background-color: transparent;
}
&:active {
color: mix($--color-black, $--color-primary, $--button-active-shade-percent);
border-color: transparent;
background-color: transparent;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# gulp-sass的使用
gulp-sass
插件能实时监测scss
代码检查其语法错误并将其编译成css
代码,帮助开发人员检查scss
语法的准确性,且其是否符合我们的预期,相关配置如下:
gulp.task('gulpsass', function() {
return gulp.src('src/style/components/hwIcon.scss')
.pipe(gulpsass().on('error', gulpsass.logError))
.pipe(gulp.dest('src/style/dest'));
});
gulp.task('watch', function() {
gulp.watch('src/style/components/hwIcon.scss', ['gulpsass']);
});
2
3
4
5
6
7
8
9
# Vue 篇
# Prop 定义尽量详细。
prop
的定义应该尽量详细,至少需要指定其类型。
// bad
props: ['status']
// good
props: {
status: String
}
// better
props: {
status: {
type: String,
required: true,
validator: function (value) {
return ['syncing','synced','version-conflict','error'].indexOf(value) !== -1
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# v-for 遍历必须添加 key
在列表数据进行遍历渲染时,需要为每一项 item
设置唯一 key
值,方便 Vue.js 内部机制精准找到该条列表数据。当 state
更新时,新的状态值和旧的状态值对比,较快地定位到 diff
。
<!-- 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>
2
3
4
5
6
7
8
9
10
# v-if 和 v-for 不要用在同一个元素上。
v-for
比 v-if
优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。
<!-- bad -->
<ul>
<li v-for="user in users" v-if="shouldShowUsers" :key="user.id">{{ user.name }}</li>
</ul>
<!-- good -->
<ul v-if="shouldShowUsers">
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
2
3
4
5
6
7
8
9
10
# 组件的 data 必须是一个函数
JS 中的实例是通过构造函数来创建的,每个构造函数可以 new 出很多个实例,那么每个实例都会继承原型上的方法或属性。Vue 的 data 数据其实是 Vue 原型上的属性,数据存在于内存当中。
同一个组件被复用多次,会创建多个实例。这些实例用的是同一个构造函数,如果 data 是一个对象的话。那么所有组件都共享了同一个对象。为了保证组件的数据独立性,要求每个组件必须通过 data 函数返回一个对象作为组件的状态,这样每复用一次组件,就会返回一份新的 data。
// bad
Vue.component('some-comp', {
data: {
foo: 'bar',
},
})
// good
Vue.component('some-comp', {
data: function () {
return {
foo: 'bar',
}
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 组件模板应该书写简洁
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
// bad
{{
fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
// good
// 在模板中
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 指令缩写
<!-- bad -->
<input v-bind:value="newTodoText" :placeholder="newTodoInstructions" v-on:input="onInput" />
<!-- good -->
<input :value="newTodoText" :placeholder="newTodoInstructions" @input="onInput" />
2
3
4
5
# 组件名为多个单词
我们开发过程中自定义的组件的名称需要为多个单词,这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
// good
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
// bad
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 多个属性进行分行
在 JavaScript 中,用多行分隔对象的多个属性是很常见的最佳实践,因为这样更易读。
<!-- good -->
<MyComponent
foo="a"
bar="b"
baz="c"
/>
<!-- bad -->
<MyComponent foo="a" bar="b" baz="c" />
2
3
4
5
6
7
8
9
10
# 元素特性的顺序
原生属性放前面,指令其次,传参和方法放最后
- class, id, ref
- name, data-*, src, alt, for, type, href, value, max, min
- title, placeholder, aria-*, role
- required, readonly, disabled
- v-model, v-for, key, v-if, v-show, v-bind,:
- foo="a" bar="b" baz="c"
2
3
4
5
6
7
# 关于组件内样式
为组件样式设置作用域
/* bad */
<style>
.btn-close {
background-color: red;
}
</style>
/* good */
<style scoped>
.button-close {
background-color: red;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
若要改变第三方组件库的样式,需要加上顶级作用域。
/* bad */
.ivu-input {
width: 254px !important;
}
/* good */
.customerForm .ivu-input {
width: 254px !important;
}
/* .customerForm为当前组件的顶级dom */
2
3
4
5
6
7
8
9
10
11
# 关于组件结构
组件结构遵循从上往下 template,script,style 的结构。
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped></style>
2
3
4
5
6
7
8
9
10
script 部分各方法成员遵循以下顺序放置。
- name
- components
- props
- data
- methods
- computed
- watch
- created
- mounted
- update
2
3
4
5
6
7
8
9
10
11
# 清除定时器或者事件监听
由于项目中有些页面难免会碰到需要定时器或者事件监听。但是在离开当前页面的时候,定时器如果不及时合理地清除,会造成业务逻辑混乱甚至应用卡死的情况,这个时就需要清除定时器事件监听,即在页面卸载(关闭)的生命周期函数里,清除定时器。
methods:{
resizeFun () {
this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128
},
setTimer() {
this.timer = setInterval(() => { })
},
clearTimer() {
clearInterval(this.timer)
this.timer = null
}
},
mounted() {
this.setTimer()
window.addEventListener('resize', this.resizeFun)
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeFun)
this.clearTimer()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。
{
path: '/Home',
component: () => import('@/views/Home.vue')
}
2
3
4
5
# 职责单一
任何时候尽量是的一个函数就做一件事情,而不是将各种逻辑全部耦合在一起,提高单个函数的复用性和可读性。比如:每个页面都会在加载完成时进行数据的请求并展示到页面。
// bad
methods: {
getList1() {
// to do ...
},
getList2() {
// to do ...
}
},
created() {
this.getList1()
this.getList2()
},
// good
methods: {
// 将全部的请求行为聚合在init函数中
init() {
this.getList1()
this.getList2()
},
getList1() {
// to do ...
},
getList2() {
// to do ...
}
},
created() {
this.init();
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 第三方 UI 组件按需引入
我们在项目中使用的第三方 UI 组件,如果我们直接引入整个组件库,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 vant 为例:
1、首先,安装 babel-plugin-component
npm install babel-plugin-component -D
2、修改 .babelrc
{
"plugins": [
["import", {
"libraryName": "vant",
"libraryDirectory": "es",
"style": true
}]
]
}
2
3
4
5
6
7
8
9
3、引入部分组件:
import Vue from 'vue'
import { Button } from 'vant'
Vue.use(Button)
2
3
4
# 组件名为多个单词
我们开发过程中自定义的组件的名称需要为多个单词,这样做可以避免跟现有的以及未来的HTML
元素相冲突,因为所有的 HTML
元素名称都是单个单词的。
推荐:
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
2
3
4
5
6
7
8
不推荐:
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
2
3
4
5
6
7
8
9
10
# 组件的 data 必须是一个函数
当在组件中使用 data
属性的时候 (除了 new Vue
外的任何地方),它的值必须是返回一个对象的函数。 因为如果直接是一个对象的话,子组件之间的属性值会互相影响。
推荐:
export default {
data () {
return {
foo: 'bar'
}
}
}
2
3
4
5
6
7
不推荐:
export default {
data: {
foo: 'bar'
}
}
2
3
4
5
# Prop定义应该尽量详细
prop
的定义应该尽量详细,至少需要指定其类型。
推荐:
props: {
status: String
}
// 更好的做法!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
不推荐:
props: ['status']
# 为 v-for 设置键值
v-for
中总是有设置 key
值。在组件上总是必须用 key
配合 v-for
,以便维护内部组件及其子树的状态。
推荐:
<ul>
<li
v-for="todo in todos"
:key="todo.id">
{{ todo.text }}
</li>
</ul>
2
3
4
5
6
7
不推荐:
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
2
3
4
5
# 完整单词的组件名
组件名应该倾向于完整单词而不是缩写,编辑器中的自动补全已经让书写长命名的代价非常之低了,而其带来的明确性却是非常宝贵的。不常用的缩写尤其应该避免。
推荐:
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
2
3
不推荐:
components/
|- SdSettings.vue
|- UProfOpts.vue
2
3
# 多个特性元素的每个特性分行
在 JavaScript
中,用多行分隔对象的多个属性是很常见的最佳实践,因为这样更易读。
推荐:
<MyComponent
foo="a"
bar="b"
baz="c"
/>
2
3
4
5
不推荐:
<MyComponent foo="a" bar="b" baz="c"/>
# 模板中简单的表达式
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
推荐:
<!-- 在模板中 -->
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
2
3
4
5
6
7
8
9
10
11
不推荐:
{{
fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
2
3
4
5
# 5.8、简单的计算属性
应该把复杂计算属性分割为尽可能多的更简单的属性。
推荐:
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
2
3
4
5
6
7
8
9
10
11
不推荐:
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
2
3
4
5
6
7
8
9
# 指令缩写
指令推荐都使用缩写形式,(用 : 表示v-bind:
、用 @ 表示v-on:
和用 # 表示 v-slot:
)。
推荐:
<input
@input="onInput"
@focus="onFocus"
>
2
3
4
不推荐:
<input
v-on:input="onInput"
@focus="onFocus"
>
2
3
4
# 标签顺序保持一致
单文件组件应该总是让标签顺序保持为 、