前端规范系列
命名规范
- 避免单字母的名字。用你的命名来描述功能(最好使用动词命名)。
function query(){
// ...
}
- 在命名对象、函数和实例时使用驼峰命名法(camelCase)。
const thisIsMyObject={};
function thisIsMyFunction(){}
- 只有在命名构造器或者类的时候才用帕斯卡拼命名法。(PascalCase每个单词的第一个字母都大写)。
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
- 不要使用前置或者后置下划线。
- 文件名字应该和默认导出的命称完全匹配。
class CheckBox {
// ...
}
export default CheckBox;
export default function fortyTwo() { return 42; }
import CheckBox from './CheckBox'; // PascalCase export/import/filename
import fortyTwo from './fortyTwo'; // camelCase export/import/filename
- 当你导出一个构造器/类/单例/函数库/暴露的对象时应该使用帕斯卡命名法。
const AirbnbStyleGuide = {
es6: {
},
};
export default AirbnbStyleGuide;
- 缩略词和缩写都必须是全部大写或者全部小写。
import SMSContainer from './containers/SMSContainer';
const HTTPRequests = [
// ...
];
const httpRequests = [
// ...
];
变量
- 使用const或者let来定义变量。避免污染全局命名空间。(最好使用名次命名)把const声明的放在一起,把let声明的放在一起,这在后边如果需要根据前边的赋值变量制定一个变量时很有用。
const superPower = new SuperPower();
const goSportsTeam = true;
let dragonball;
let i;
- 在你需要的使用定义变量,但是要把它放在一个合理的位置。let 和 const 是块级作用域而不是函数作用域。
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
- 不要链式变量赋值。链式变量赋值会创建隐式全局变量。
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// 对于 `const` 也一样
注释
- 使用/**…*/来进行多行注释。
/**
1. make() returns a new element
2. based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
2.使用 // 进行单行注释。 将单行注释放在需要注释的行的上方新行。 在注释之前放一个空行,除非它在块的第一行。
// 注释
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
空格
- 使用tabs(空格字符)设置为2个空格。
function baz() {
∙∙let name;
}
- 在主体前放置一个空格。
function test() {
console.log('test');
}
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
- 在控制语句(if ,while 等)开始括号之前放置一个空格。在函数调用和声明中,在参数列表和函数名之间没有空格。
if (isJedi) {
fight();
}
function fight() {
console.log('Swooosh!');
}
- 用空格分离操作符。
const x = y + 5;
- 在块和下个语句之前留下一空白行。
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
- 不要在括号内添加空格。
if (foo) {
console.log(foo);
}
- 不要在中括号中添加空格
const foo = [1, 2, 3];
console.log(foo[0]);
- 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强 制实施一致的间距。
function foo() { return true; }
if (foo) { bar = 0; }
- 在逗号之前避免使用空格,逗号之后使用空格。
var foo = 1, bar = 2;
var arr = [1, 2];
- 在行的末尾避免使用空格。
分号
- 每一行的
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
jedi.father = 'vader';
});
方法
- 使用命名的函数表达式代替函数声明。 函数声明是挂起的,这意味着在它在文件中定义之前,很容易引用函数。这会损害可读性和可维护性。 // 从变量引用调用中区分的词汇名称。
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
- Wrap立即调用函数表达式。 立即调用的函数表达式是单个单元 - 包装, 并且拥有括号调用, 在括号内, 清晰的表达式。
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
-
切记不要在非功能中声明函数(if ,while,等)。将函数赋值给变量。
-
避免使用默认参数的副作用。他们很容易混淆。
var b = 1;
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
- 总是把默认参数放在最后。
function handleThings(name, opts = {}) {
// ...
}
- 函数签名中的间距。 一致性很好,在删除或添加名称时不需要添加或删除空格。
const x = function () {};
const y = function a() {};
- 优先使用扩展运算符 … 来调用可变参数函数 它更加干净,你不需要提供上下文。
const x = [1, 2, 3, 4, 5];
console.log(...x);
new Date(...[2016, 8, 5]);
箭头函数
- 当你必须使用匿名函数的时候(当传递内联动函数时),使用箭头函数。它创建了一个在 this 上下文中执行的函数版本,它通常是你想要的,并且是一个更简洁的语法。如果你有一个相当复杂的函数,你可以把这个逻辑转移到它自己的命名函数表达式中。
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 如果表达式跨越多个行,用括号将其括起来,以获得更好的可读性。 它清楚地显示了函数的起点和终点。
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
- 如果你的函数接收一个参数,则可以不用括号,省略括号。 否则,为了保证清晰和一致性,需要在参数周围加上括号。 注意:总是使用括号是可以接受的,在这种情况下,我们使用 “always” option 来配置减少视觉上的混乱。
[1, 2, 3].map(x => x * x);
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 避免箭头函数符号 (=>) 和比较运算符 (<=, >=) 的混淆。
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
- 注意带有隐式返回的箭头函数函数体的位置。
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
)
类和构造器
- 尽量使用 class,避免直接操作prototype .class语法更简洁,更容易推理。
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
- 使用 extends 来扩展继承。它是一个内置的方法,可以在不破坏 instanceof 的情况下继承原型功能。
class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}
- 方法返回了 this 来提供其内部方法调用。
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
- 如果没有指定类,则类具有默认的构造器。一个空的构造器或是一个代表父类的函数是没有必要的。
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
- 避免定义重复的类成员。重复的类成员声明将会默认倾向于最后一个-具有重复的类成员可以说是一个错误。
class Foo {
bar() { return 1; }
}
class Foo {
bar() { return 2; }
}
模块
- 你可能经常使用模块(import/export)在一些非标准模块的系统上。你也可以在你喜欢的模块系统上相互转换。模块是未来的趋势,让我们拥抱未来。
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
import { es6 } from './AirbnbStyleGuide';
export default es6;
- 不要使用通配符导入。这确定你有一个单独的默认导出。
import AirbnbStyleGuide from './AirbnbStyleGuide';
- 不要直接导入导出,虽然写在一行很简洁,但是有一个明确的导入和一个明确的导出能够保证一致性。
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
- 只从一个路径导入所有需要的东西。从同一个路径导入多个行,使代码更难以维护。
import foo, { named1, named2 } from 'foo';
import foo, {
named1,
named2,
} from 'foo';
- 不要导出可变的引用。在一般情况下,应该避免发生突变,但是在导出可变引用时及其容易发生突变。虽然在某些特殊情况下,可能需要这样,但是一般情况下只需要导出常量引用。
const foo = 3;
export { foo };
- 在单个导出的模块中,选择默认模块而不是指定导出。为了鼓励更多的文件只导出一件东西,这样可读性和可维护性更好。
export default function foo() {}
- 将所有的imports语句放在所有非导入语句的上边。由于所有的imports都被提前,保持他们在顶部是为了防止意外发生。
import foo from 'foo';
import bar from 'bar';
foo.init();
- 多行导入应该像多行数组和对象一样缩进。花括号和其他规范一样,遵循相同的缩进规则,后边的都好一样。
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
- 在模块导入语句禁止使用 Webpack 加载器语法。因为在导入语句中使用 webpack 语法,将代码和模块绑定在一起。应该在 webpack.config.js中使用加载器语法。
import fooSass from 'foo.scss';
import barCss from 'bar.css';
属性
- 访问属性时使用点符号。
const luke = { jedi: true, age: 28, }; const isJedi = luke.jedi;
- 使用变量访问属性时,使用[ ]表示法。
const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi');
- 计算指数时,可以使用 ** 运算符。
const binary = 2 ** 10;
比较运算符合和等号
- 使用 === 和 !== 而不是 == 和 !=。
- 条件语句,例如 if 语句使用 ToBoolean 的抽象方法来计算表达式的结果,并始终遵循以下简单的规则: Objects 的取值为: true Undefined 的取值为: false Null 的取值为: false Booleans 的取值为: 布尔值的取值 Numbers 的取值为:如果为 +0, -0, or NaN 值为 false 否则为 true Strings 的取值为: 如果是一个空字符串 ‘’ 值为 false 否则为 true
if ([0] && []) {
// true
// 一个数组(既是是空的)是一个对象,对象的取值为 true
}
- 对于布尔值使用简写,但是对于字符串和数字进行显式比较。
if (isValid) {
// ...
}
if (collection.length > 0) {
// ...
}
-
获取更多信息请查看 Angus Croll 的 Truth Equality and JavaScript 。
-
在 case 和 default 的子句中,如果存在声明 (例如. let, const, function, 和 class),使用大括号来创建块 。
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
const y = 2;
break;
}
case 3: {
function f() {
// ...
}
break;
}
}
- 三目表达式不应该嵌套,通常是单行表达式。
// 分离为两个三目表达式
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
- 避免不必要的三目表达式。
const foo = a || b;
const bar = !!c;
const baz = !c;
- 使用该混合运算符时,使用括号括起来。 唯一例外的是标准算数运算符 (+, -, *, & /) 因为他们的优先级被广泛理解。 这能提高可读性并且表明开发人员的意图。
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
const bar = (a ** b) - (5 % d);
if (a || (b && c)) {
return d;
}
const bar = a + b / c * d;
块
- 当有多行代码块的时候,使用大括号包裹。
if (test) return false;
if (test) {
return false;
}
- 如果你使用的是 if 和 else 的多行代码块,则将 else 语句放在 if 块闭括号同一行的位置。
if (test) {
thing1();
thing2();
} else {
thing3();
}
- 如果一个 if 块总是执行一个 return 语句,那么接下来的 else 块就没有必要了。 如果一个包含 return 语句的 else if 块,在一个包含了 return 语句的 if 块之后,那么可以拆成多个 if 块。
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
ESlint
module.exports = {
//此项是用来告诉eslint找当前配置文件不能往父级查找
root: true,
//此项是用来指定eslint解析器的,解析器必须符合规则,babel-eslint解析器是对babel解析器的包装使其与ESLint解析
parser: 'babel-eslint',
//此项是用来指定javaScript语言类型和风格,sourceType用来指定js导入的方式,默认是script,此处设置为module,指某块导入方式
parserOptions: {
// 设置 script(默认) 或 module,如果代码是在ECMASCRIPT中的模块
sourceType: 'module',
"ecmaVersion": 6,
"ecmaFeatures": {
"jsx": true
}
},
// 此项指定环境的全局变量,下面的配置指定为浏览器环境
env: {
"browser": true,
"node": true,
"commonjs": true,
"es6": true,
"amd": true
},
// 此项是用来配置标准的js风格,就是说写代码的时候要规范的写,如果你使用vs-code我觉得应该可以避免出错
extends: 'vue',
// 此项是用来提供插件的,插件名称省略了eslint-plugin-,下面这个配置是用来规范html的
plugins: [
'html',
"flow-vars",
"react"
],
/*
下面这些rules是用来设置从插件来的规范代码的规则,使用必须去掉前缀eslint-plugin-
主要有如下的设置规则,可以设置字符串也可以设置数字,两者效果一致
"off" -> 0 关闭规则
"warn" -> 1 开启警告规则
"error" -> 2 开启错误规则
*/
rules: {
// 警告
"no-extra-parens": 1, // 非必要的括号
"no-empty": 1, // 块语句中的内容不能为空
"no-use-before-define": [1, "nofunc"], // 未定义前不能使用
"no-unused-vars": 1, // 不能有声明后未被使用的变量或参数
"no-undef": 1, // 不可以 有未定义的变量
// vue
"flow-vars/define-flow-type": 1,
"flow-vars/use-flow-type": 1,
// react
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
// 代码风格
"no-multi-spaces": 1, // 不能用多余的空格
"key-spacing": [1, { // 对象字面量中冒号的前后空格
"beforeColon": false,
"afterColon": true
}],
"block-scoped-var": 2, // 块语句中使用var
"consistent-return": 2, // return 后面是否允许省略
"accessor-pairs": 2, // 在对象中使用getter/setter
"no-return-assign": [2, "always"], // return 语句中不能有赋值表达式
"no-redeclare": [2, { // 禁止重复声明变量
"builtinGlobals": true
}],
"space-infix-ops": 2, // 中缀操作符周围要不要有空格
"curly": 1, // 必须使用 if(){} 中的{}
// common js
"no-duplicate-imports": 1
}
}
ESlint2
"no-caller": 1,//禁止使用arguments.caller或arguments.callee
"no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
"no-empty": 2,//块语句中的内容不能为空
"no-eq-null": 2,//禁止对null使用==或!=运算符
"no-eval": 1,//禁止使用eval
"no-extra-parens": 2,//禁止非必要的括号
"no-extra-semi": 2,//禁止多余的冒号
"no-func-assign": 2,//禁止重复的函数声明
"no-inline-comments": 0,//禁止行内备注
"no-label-var": 2,//label名不能与var声明的变量名相同
"no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
"linebreak-style": [0, "windows"],//换行风格
"no-multi-spaces": 1,//不能用多余的空格
"no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
"no-redeclare": 2,//禁止重复声明变量
"no-return-assign": 1,//return 语句中不能有赋值表达式
"no-self-compare": 2,//不能比较自身
"no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
"no-trailing-spaces": 1,//一行结束后面不要有空格
"no-this-before-super": 0,//在调用super()之前不能使用this或super
"no-undef": 1,//不能有未定义的变量
"no-underscore-dangle": 1,//标识符不能以_开头或结尾
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
"no-use-before-define": 2,//未定义前不能使用
"no-var": 0,//禁用var,用let和const代替
"array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
"arrow-parens": 0,//箭头函数用小括号括起来
"arrow-spacing": 0,//=>的前/后括号
"accessor-pairs": 0,//在对象中使用getter/setter
"camelcase": 2,//强制驼峰法命名
"consistent-this": [2, "that"],//this别名
"default-case": 2,//switch语句最后必须有default
"eqeqeq": 2,//必须使用全等
"func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
"indent": [2, 4],//缩进风格
目录规范
├── build // 构建相关
├── config // 配置相关
├── src // 源代码
│ ├── api // 所有请求
│ ├── assets // 主题 字体等静态资源
│ ├── components // 全局公用组件
│ ├── directive // 全局指令
│ ├── filters // 全局 filter
│ ├── icons // 项目所有 svg icons
│ ├── lang // 国际化 language
│ ├── mock // 项目mock 模拟数据
│ ├── router // 路由
│ ├── store // 全局 store管理
│ ├── styles // 全局样式
│ ├── utils // 全局公用方法
│ ├── vendor // 公用vendor
│ ├── views // views 所有页面
│ ├── App.vue // 入口页面
│ ├── main.js // 入口文件 加载组件 初始化等
│ └── permission.js // 权限管理
├── static // 第三方不打包资源
│ └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── .eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── .travis.yml // 自动化CI配置
├── favicon.ico // favicon图标
├── index.html // html模板
└── package.json // package.json