JS


2018-11-4 JS

ES6 编码

ES6 简介

ECMAScript 6 简称 ES6,是 JavaScript 语言的下一代标准,ECMAScript 是JavaScript 的语法规格,JavaScript 是ECMAScript 的一种实现

let、const

let 定义的变量不会被变量提升,const定义常量, 定义的常量不能被修改,let 和 const 都是块级作用域

import、export

import导入模块、export导出模块
导出的对象必须与模块中的值一一对应,换一种说法就是导出的对象与整个模块进行结构赋值。

// 全部导入
import people from './people'
  
// 将整个模块当作单一对象进行导入,该模块的所有导出都会作为对象的属性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.getName())
  
// 导入部分,引入非 default 时,使用花括号
import {name, age} from './example'
  
  
// 导出默认, 有且只有一个默认
export default App
  
// 部分导出
export class App extend Component {};
 
 
//a.js中
export default{    //(最常使用的方法,加入default关键字代表在import时可以使用任意变量名并且不需要花括号{})
     a: function(){
         console.log(666)
   }
}
 
export function(){  //导出函数
 
}
 
export {newA as a ,b,c}  //  解构赋值语法(as关键字在这里表示将newA作为a的数据接口暴露给外部,外部不能直接访问a)
 
//b.js中
import  a  from  '...'  //import常用语法(需要export中带有default关键字)可以任意指定import的名称
 
import {...} from '...'  // 基本方式,导入的对象需要与export对象进行解构赋值。
 
import a as biubiubiu from '...'  //使用as关键字,这里表示将a代表biubiubiu引入(当变量名称有冲突时可以使用这种方式解决冲突)
 
import {a as biubiubiu,b,c}  //as关键字的其他使用方法
1
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
33
34
35
36
37
38
39
40

class、extends、super

class Animal {
    constructor() {
        this.type = 'animal';
    }
    says(say) {
        console.log(this.type + ' says ' + say);
    }
}
  
let animal = new Animal();
animal.says('hello'); //animal says hello
  
class Cat extends Animal {
    constructor() {
        super();
        this.type = 'cat';
    }
}
  
let cat = new Cat();
cat.says('hello'); //cat says hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

constructor方法,是构造方法,this关键字代表实例对象。constructor内定义的方法和属性是实例对象自己的,constructor外定义的方法和属性是所有实力对象可以共享的。
Class之间可以通过extends关键字实现继承
super关键字,指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

arrow functions (箭头函数)

函数的快捷写法。不需要 function 关键字来创建函数,省略 return 关键字,继承当前上下文的 this 关键字
当你的函数有且仅有一个参数的时候,是可以省略掉括号的;当你函数中有且仅有一个表达式的时候可以省略{}

class Home extends React.Component{
    constructor(props){
        super(props)
        this.state={
            msg: 'aaa'
        }
    }
    setData=()=>{
        this.setState({
            msg:"bbb"
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

template string (模板字符串)

实现字符串拼接,多行字符串拼接,用 和${}

this.$http(`${st === false ? SQU_API.SUM_MATERIAL_UPDATE_API : SQU_API.SUM_MATERIAL_SUBMIT_API}`, 'POST', tmp)
1

includes和repeat

// includes:判断是否包含然后直接返回布尔值 let str = 'hahah'; console.log(str.includes('y')); // false

// repeat: 获取字符串重复n次 let s = 'he'; console.log(s.repeat(3)); // 'hehehe'

destructuring (解构)

简化数组和对象中信息的提取

let people2 = {
    name: 'ming',
    age: 20,
    color: ['red', 'blue']
}
  
let { name, age } = people2;
let [first, second] = people2.color;
1
2
3
4
5
6
7
8

函数默认参数

function foo(num = 200) {
    return num;
}
1
2
3

rest参数 展开运算符

常用传参比较多的方法不确定的参数时,结合展开运算符

function foo(x, y, ...rest) {
    return ((x + y) * rest.length);
}
foo(1, 2, 'hello', true, 7); // 9
 
 
let color = ['red', 'yellow'];
let colorful = [...color, 'green', 'blue'];
console.log(colorful); // ["red", "yellow", "green", "blue"]
 
 
let num = [1, 3, 5, 7, 9];
let [first, second, ...rest] = num;
console.log(rest); // [5, 7, 9]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

简写

// 对象初始化简写
function people(name, age) {
    return {
        name,
        age
    };
}
 
 
// 对象字面量简写
let people2 = {
    name: 'bai',
    getName () {
        console.log(this.name);
    }
};
 
 
// Object.assign() 实现浅复制 Object.assign()可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,我们为了不改变源对象。一般会把目标对象传为{}
const obj = Object.assign({}, objA, objB)
// 给对象添加属性
this.seller = Object.assign({}, this.seller, response.data)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Promise

用同步的方式去写异步代码 最常用的

let updateMaterial = new Promise((resolve, reject) => {
  that.$refs.materielref.updateMaterial(str, resolve, reject)
})
let UpdateTime = new Promise((resolve, reject) => {
  that.$refs.manhour.UpdateTime(str, resolve, reject)
})
let saveNet = Promise.all([updateMaterial, UpdateTime])
saveNet.then(() => {})
1
2
3
4
5
6
7
8

Async/await es7

函数前面的async,这个函数总是返回一个promise,如果代码中有return <非promise>语句,JavaScript会自动把返回的这个value值包装成promise的resolved值。
关键词await可以让JavaScript进行等待,直到一个promise执行并返回它的结果,JavaScript才会继续往下执行。

async (ctx)=>{
  const user = ctx.request.body
  const userSigned = await UserModel.findOne({
    where: {
      username: user.username
    }
  })
  if (!userSigned) {
    ctx.body = {
      code:500,
      msg:'用户不存在'
    }
    return
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

async 模块
串行无关联
async.series([], callback)
执行时间等于里面执行的和
并行无关联
async.parallel([], callback)
执行时间等于里面最长的一个
串行有关联
async.waterfall([], callback)

Generators

生成器( generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就是比普通的function多了个星号*,在其函数体内可以使用yield关键字,函数会在每个yield后暂停。
迭代器对异步编程作用很大,异步调用对于我们来说是很困难的事,我们的函数并不会等待异步调用完再执行,你可能会想到用回调函数,(当然还有其他方案比如Promise比如Async/await)。
生成器可以让我们的代码进行等待。就不用嵌套的回调函数。使用generator可以确保当异步调用在我们的generator函数运行一下行代码之前完成时暂停函数的执行。
迭代器:当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有一个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的对象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止提供值。

// 生成器
function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}
  
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
  
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
function run(taskDef) {
    // taskDef 即一个生成器函数
    // 创建迭代器,让它在别处可用
    let task = taskDef();
  
    // 启动任务
    let result = task.next();
  
    // 递归使用函数来保持对 next() 的调用
    function step() {
        // 如果还有更多要做的
        if (!result.done) {
            result = task.next();
            step();
        }
    }
  
    // 开始处理过程
    step();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

求幂运算符

3 ** 2  //9
效果同
Math.pow(3, 2) //9
1
2
3

Object.entries()

将一个对象中可枚举属性的键名和键值按照二维数组的方式返回。若对象是数组,则会将数组的下标作为键值返回。如果对象的key值是数字,则返回值会对key值进行排序,返回的是排序后的结果

Object.entries({ one: 1, two: 2 })    //[['one', 1], ['two', 2]]
Object.entries([1, 2])                //[['0', 1], ['1', 2]]
Object.entries({[Symbol()]:1, two: 2})  //[['two', 2]] Symbol编译时会被自动忽略
Object.entries({ 3: 'a', 4: 'b', 1: 'c' })    //[['1', 'c'], ['3', 'a'], ['4', 'b']]
 
 
    var obj = { foo: 'bar', baz: 42 };
     
    var map1 = new Map([['foo', 'bar'], ['baz', 42]]); //原本的创建方式
    var map2 = new Map(Object.entries(obj));    //等同于map1
 
    console.log(map1);// Map { foo: "bar", baz: 42 }
    console.log(map2);// Map { foo: "bar", baz: 42 }
1
2
3
4
5
6
7
8
9
10
11
12
13

Object.values()

只返回自己的键值对中属性的值。它返回的数组顺序,也跟Object.entries()保持一致

Object.values({ one: 1, two: 2 })            //[1, 2]
Object.values({ 3: 'a', 4: 'b', 1: 'c' })    //['c', 'a', 'b']
1
2

padStart()和padEnd()

字符串填充 String.padStart(targetLength, padding)

'Vue'.padEnd(10, '_*')           //'Vue_*_*_*_'
'React'.padEnd(10, 'Hello')      //'ReactHello'
'JavaScript'.padEnd(10, 'Hi')    //'JavaScript'
'JavaScript'.padEnd(8, 'Hi')     //'JavaScript'
1
2
3
4

修饰器Decorator

一个函数包装成另一个函数,这样的方式称之为“修饰器”
类修饰器 装饰器是在编译时就执行的函数

@addSkill
class Person { }
 
function addSkill(target) {
    target.say = "hello world";
}
// 给目标对象添加额外的属性say
1
2
3
4
5
6
7

方法修饰器

class Person {
    constructor() {}
    @myname  //方法修饰器
    name() {
        console.log('1')
    }
}
function myname(target, key, descriptor) {
    console.log(target);
    console.log(key);
    console.log(descriptor);
    descriptor.value = function() {
        console.log('1')
    }
}
// target: 类的原型对象,上例是Person.prototype
// key: 所要修饰的属性名  name
// descriptor: 该属性的描述对象
 
var personOne = new Person() //实例化
personOne.name() //调用name()方法
 
 
//打印结果:
Person {}
name
{ value: [Function: name],
  writable: true,
  enumerable: false,
  configurable: true
 }
1
1
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

若是同一个方法上有多个修饰器,先从外到内进入,然后由内向外执行。

lass Person {
    constructor() {}
    @dec(1)
    @dec(2)
    name() {
        console.log('end')
    }
}
function dec(id) {
    console.log('out', id);
    return function(target, key, descriptor) {
        console.log(id);
    }
}
 
var person = new Person()
person.name()
//结果
out 1
out 2
2
1
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

javascript 规范

JS ESlint

安装
npm install --save-dev eslint eslint-config-airbnb-base@latest eslint-plugin-import eslint-plugin-html eslint-plugin-vue babel-eslint  eslint-plugin-vue
// or
yarn add --dev eslint eslint-config-airbnb-base@latest eslint-plugin-import eslint-plugin-html eslint-plugin-vue babel-eslint eslint-plugin-vue
eslint --init // 根目录建立设置文件 .eslintrc.js
1
2
3
4
在当前项目根目录下添加 .editorconfig 配置文件
root = true                       #// 表示当前是项目根目录
 
 
[*]                               #// 所有文件都使用配置
charset = utf-8                   #// 编码格式
indent_style = tab                #// Tab 键缩进的样式,由 space 和 table 两种 一般代码中是 space
indent_size = 2                   #// 缩进 size 为 2
end_of_line = lf                  #// 以 lf 换行,默认 win 为 crlf,mac 和 linux 为 lf
insert_final_newline = true       #// 末尾加一行空行
trim_trailing_whitespace = true   #// 去掉行尾多余空格
 
# The indent size used in the `package.json` file cannot be changed
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
[{*.yml,*.yaml,package.json}]
indent_style = space
indent_size = 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
当前项目根目录下添加 .eslintrc.js 配置文件
// 配置文件內容 手持,ipad,PC 皆适用
module.exports = {
  // Eslint检测配置文件步骤:
  // 1.在要检测的文件同一目录里寻找 .eslintrc.* 和 package.json
  // 2.紧接着在父级目录里寻找,一直到文件系统的根目录
  // 3.如果在前两步发现有 root:true 的配置,停止在父级目录中寻找.eslintrc
  // 4.如果以上步骤都没有找到,则回退到用户主目录 ~/.eslintrc 中自定义的默认配置
  root: true,
  parserOptions: {
    parser: 'babel-eslint', // ESLint 默认使用 Espree 作为其解析器,babel-eslint 也是用得最多的解析器
    sourceType: 'module', // 设置为 script (默认) 或 module(如果你的代码是 ECMAScript 模块)
  },
  // 指定环境,每个环境都有自己预定义的全局变量,可以同时指定多个环境,不矛盾
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  // plugin 与 extend 的区别:extend 提供的是 ESLint 现有规则的一系列预设
  // 而 plugin 则提供了除预设之外的自定义规则,当你在 eslint 的规则里找不到合适的的时候
  extends: [
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'plugin:vue/recommended', // 严格模式
    // https://github.com/standard/standard/blob/master/docs/RULES-zhcn.md
    'standard', // 采用 javascript standard 规范 or airbnb-base
  ],
  // required to lint *.vue files
  plugins: ['vue'],
  // 具体规则配置
  // off 或 0-- 关闭规则
  // warn 或 1-- 开启规则,警告级别(不会导致程序退出)
  // error 或 2-- 开启规则,错误级别(当被触发的时候,程序会退出)
  rules: {
    'vue/max-attributes-per-line': [2, { singleline: 10, multiline: { max: 1, allowFirstLine: false } }],
    'vue/name-property-casing': ['error', 'PascalCase'],
    'no-tabs': 'off', // tab 缩排的使用
    'semi': [0, 'always'], // 语句强制分号结尾
    'no-alert': 0, // 禁止使用alert confirm prompt
    'no-array-constructor': 2, // 禁止使用数组构造器
    'no-bitwise': 0, // 禁止使用按位运算符
    'no-caller': 2, // 禁止使用 arguments.caller 或 arguments.callee
    'no-catch-shadow': 2, // 禁止catch子句参数与外部作用域变量同名
    'no-class-assign': 2, // 禁止给类赋值
    'no-cond-assign': 2, // 禁止在条件表达式中使用赋值语句
    'no-console': 0, // 禁止使用console
    'no-const-assign': 2, // 禁止修改const声明的变量
    'no-constant-condition': 2, // 禁止在条件中使用常量表达式 if(true) if(1)
    'no-continue': 0, // 禁止使用continue
    'no-control-regex': 2, // 禁止在正则表达式中使用控制字符
    'no-delete-var': 2, // 不能对var声明的变量使用delete操作符
    'no-div-regex': 1, // 不能使用看起来像除法的正则表达式/=foo/
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2, // 在创建对象字面量时不允许键重复 {a:1,a:1}
    'no-dupe-args': 2, // 函数参数不能重复
    'no-duplicate-case': 2, // switch中的case标签不能重复
    'no-else-return': 2, // 如果if语句里面有return,后面不能跟else语句
    'no-empty': 2, // 块语句中的内容不能为空
    'no-empty-character-class': 2, // 正则表达式中的[]内容不能为空
    'no-empty-pattern': 2,
    'no-empty-label': 0, // 禁止使用空 label
    'no-eq-null': 2, // 禁止对null使用==或!=运算符
    'no-eval': 2, // 禁止使用eval
    'no-ex-assign': 2, // 禁止给catch语句中的异常参数赋值
    'no-extend-native': 2, // 禁止扩展native对象
    'no-extra-bind': 2, // 禁止不必要的函数绑定
    'no-extra-boolean-cast': 2, // 禁止不必要的bool转换
    'no-extra-parens': 2, // 禁止非必要的括号
    'no-extra-semi': 2, // 禁止多余的冒号
    'no-fallthrough': 2, // 禁止 switch 穿透
    'no-floating-decimal': 2, // 禁止省略浮点数中的0 .5 3.
    'no-func-assign': 2, // 禁止重复的函数声明
    'no-implicit-coercion': 1, // 禁止隐式转换
    'no-implied-eval': 2, // 禁止使用隐式 eval
    'no-inline-comments': 0, // 禁止行内备注
    'no-inner-declarations': [2, 'functions'], // 禁止在块语句中使用声明(变量或函数)
    'no-invalid-regexp': 2, // 禁止无效的正则表达式
    'no-invalid-this': 2, // 禁止无效的this,只能用在构造器,类,对象字面量
    'no-irregular-whitespace': 2, // 不能有不规则的空格
    'no-iterator': 2, // 禁止使用__iterator__ 属性
    'no-label-var': 2, // label 名不能与 var 声明的变量名相同
    'no-labels': [2, { allowLoop: false, allowSwitch: false }], // 禁止标签声明
    'no-lone-blocks': 2, // 禁止不必要的嵌套块
    'no-lonely-if': 2, // 禁止 else 语句内只有 if 语句
    'no-loop-func': 1, // 禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
    'no-mixed-requires': [0, false], // 声明时不能混用声明类型
    'no-mixed-spaces-and-tabs': [2, false], // 禁止混用 tab 和空格
    'linebreak-style': [0, 'windows'], // 换行风格
    'no-multi-spaces': 2, // 不能用多余的空格
    'no-multi-str': 2, // 字符串不能用\换行
    'no-multiple-empty-lines': [2, { max: 2 }], // 空行最多不能超过2行
    'no-native-reassign': 2, // 不能重写native对象
    'no-negated-in-lhs': 2, // in 操作符的左边不能有!
    'no-nested-ternary': 0, // 禁止使用嵌套的三目运算
    'no-new': 1, // 禁止在使用new构造一个实例后不赋值
    'no-new-func': 1, // 禁止使用new Function
    'no-new-object': 2, // 禁止使用new Object()
    'no-new-require': 2, // 禁止使用new require
    'no-new-symbol': 2,
    'no-new-wrappers': 2, // 禁止使用 new 创建包装实例,new String new Boolean new Number
    'no-obj-calls': 2, // 不能调用内置的全局对象,比如 Math() JSON()
    'no-octal': 2, // 禁止使用八进制数字
    'no-octal-escape': 2, // 禁止使用八进制转义序列
    'no-param-reassign': 2, //禁 止给参数重新赋值
    'no-path-concat': 0, // node中不能使用__dirname或__filename做路径拼接 v
    'no-plusplus': 0, // 禁止使用++,--
    'no-process-env': 0, // 禁止使用process.env
    'no-process-exit': 0, // 禁止使用process.exit()
    'no-proto': 2, // 禁止使用__proto__属性
    'no-redeclare': 2, // 禁止重复声明变量
    'no-regex-spaces': 2, // 禁止在正则表达式字面量中使用多个空格 /foo bar/
    'no-restricted-modules': 0, // 如果禁用了指定模块,使用就会报错
    'no-return-assign': [2, 'except-parens'], // return 语句中不能有赋值表达式
    'no-self-assign': 2,
    'no-script-url': 0, // 禁止使用 javascript:void(0)
    'no-self-compare': 2, // 不能比较自身
    'no-sequences': 2, // 禁止使用逗号运算符
    'no-shadow': 2, // 外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
    'no-shadow-restricted-names': 2, // 严格模式中规定的限制标识符不能作为声明时的变量名使用
    'no-spaced-func': 2, // 函数调用时 函数名与()之间不能有空格
    'no-sparse-arrays': 2, // 禁止稀疏数组, [1,,2]
    'no-sync': 0, // nodejs 禁止同步方法
    'no-ternary': 0, // 禁止使用三目运算符
    'no-trailing-spaces': 2, // 一行结束后面不要有空格
    'no-this-before-super': 2, // 在调用 super() 之前不能使用 this 或 super
    'no-throw-literal': 2, // 禁止抛出字面量错误 throw "error";
    'no-undef': 2, // 不能有未定义的变量
    'no-undef-init': 2, // 变量初始化时不能直接给它赋值为 undefined
    'no-undefined': 2, // 不能使用 undefined
    'no-unmodified-loop-condition': 2,
    'no-unexpected-multiline': 2, // 避免多行表达式
    'no-underscore-dangle': 1, // 标识符不能以_开头或结尾
    'no-unneeded-ternary': [2, { defaultAssignment: false }], // 禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
    'no-unreachable': 2, // 不能有无法执行的代码
    'no-unused-expressions': 2, // 禁止无用的表达式
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [2, { vars: 'all', args: 'none' }], // 不能有声明后未被使用的变量或参数
    'no-use-before-define': 2, // 未定义前不能使用
    'no-useless-call': 2, // 禁止不必要的 call 和 apply
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-void': 2, // 禁用 void 操作符
    'no-var': 1, // 禁用 var,用 let 和 const 代替
    'no-warning-comments': [1, { terms: ['todo', 'fixme', 'xxx'], location: 'start' }], //不能有警告备注
    'no-with': 2, // 禁用 with
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, // 禁止使用 debugger
    'array-bracket-spacing': [2, 'never'], // 是否允许非空数组里面有多余的空格
    'arrow-parens': 0, // 箭头函数用小括号括起来
    'arrow-spacing': [2, { before: true, after: true }], // =>的前/后括号
    'block-spacing': [2, 'always'],
    'accessor-pairs': 2, // 在对象中使用 getter/setter
    'block-scoped-var': 0, // 块语句中使用 var
    'brace-style': [2, '1tbs', { allowSingleLine: true }], // 大括号风格
    'callback-return': 1, // 避免多次调用回调什么的
    'camelcase': [0, { properties: 'always' }], // 强制驼峰法命名
    'comma-dangle': [2, 'never'], // 对象字面量项尾不能有逗号
    'comma-spacing': [2, { before: false, after: true }], // 逗号前后的空格
    'comma-style': [2, 'last'], // 逗号风格,换行时在行首还是行尾
    'complexity': [0, 11], // 循环复杂度
    'computed-property-spacing': [0, 'never'], // 是否允许计算后的键名什么的
    'consistent-return': 0, // return 后面是否允许省略
    'consistent-this': [2, 'that'], // this别名
    'constructor-super': 2, // 非派生类不能调用 super,派生类必须调用 super
    'curly': [2, 'multi-line'], // 必须使用 if(){} 中的{}
    'default-case': 2, // switch语句最后必须有 default
    'dot-location': [2, 'property'], // 对象访问符的位置,换行的时候在行首还是行尾
    'dot-notation': [0, { allowKeywords: true }], // 避免不必要的方括号
    'eol-last': 2, // 文件以单一的换行符结束
    'eqeqeq': 2, // 必须使用全等
    'func-names': 0, // 函数表达式必须有名字
    'func-style': [0, 'declaration'], // 函数风格,规定只能使用函数声明/函数表达式
    'generator-star-spacing': [2, { before: true, after: true }], // 生成器函数 * 的前后空格
    'guard-for-in': 0, // for in 循环要用 if 语句过滤
    'handle-callback-err': [2, '^(err|error)$'], // nodejs 处理错误
    'id-length': 0, // 变量名长度
    // indent: [0, 4], // 缩进风格
    'indent': [2, 2, { SwitchCase: 1 }], // 缩进风格
    'jsx-quotes': [2, 'prefer-single'],
    'init-declarations': 0, // 声明时必须赋初值
    'key-spacing': [2, { beforeColon: false, afterColon: true }], // 对象字面量中冒号的前后空格
    'keyword-spacing': [2, { before: true, after: true }],
    'lines-around-comment': 0, // 行前/行后备注
    'max-depth': [0, 4], // 嵌套块深度
    'max-len': [0, 80, 4], // 字符串最大长度
    'max-nested-callbacks': [0, 2], // 回调嵌套深度
    'max-params': [0, 5], // 函数最多只能有5个参数
    'max-statements': [0, 10], // 函数内最多有几个声明
    'new-cap': [2, { newIsCap: true, capIsNew: false }], // 函数名首行大写必须使用 new 方式调用,首行小写必须用不带 new 方式调用
    'new-parens': 2, // new 时必须加小括号
    'newline-after-var': 0, // 变量声明后是否需要空一行
    'object-curly-spacing': [2, 'always', { objectsInObjects: false }], // 大括号内是否允许不必要的空格
    'object-shorthand': 0, // 强制对象字面量缩写语法
    'one-var': [2, { initialized: 'never' }], // 连续声明
    'operator-assignment': [0, 'always'], // 赋值运算符 += -=什么的
    'operator-linebreak': [2, 'after', { overrides: { '?': 'before', ':': 'before' } }], // 换行时运算符在行尾还是行首
    'padded-blocks': 0, // 语句内行首行尾是否要空行
    'prefer-const': 2, // 首选 const
    'prefer-spread': 0, // 首选展开运算
    'prefer-reflect': 0, // 首选 Reflect 的方法
    'quotes': [2, 'single', { avoidEscape: true, allowTemplateLiterals: true }], // 引号类型 `` "" ''
    'quote-props': [0, 'always'], // 对象字面量中的属性名是否强制双引号
    'radix': 2, // parseInt必须指定第二个参数
    'id-match': 0, // 命名检测
    'require-yield': 0, // 生成器函数必须有yield
    'semi-spacing': [2, { before: false, after: true }], // 分号前后空格
    'sort-vars': 0, // 变量声明时排序
    'space-after-keywords': [0, 'always'], // 关键字后面是否要空一格
    'space-before-blocks': [2, 'always'], // 不以新行开始的块 { 前面要不要有空格
    'space-before-function-paren': [2, 'always'], // 函数定义时括号前面要不要有空格
    'space-in-parens': [2, 'never'], // 小括号里面要不要有空格
    'space-infix-ops': 2, // 中缀操作符周围要不要有空格
    'space-return-throw-case': 0, // return throw case 后面要不要加空格
    'space-unary-ops': [2, { words: true, nonwords: false }], //一元运算符的前/后要不要加空格
    'spaced-comment': 0, // 注释风格要不要有空格什么的
    'template-curly-spacing': [2, 'never'],
    'strict': 2, // 使用严格模式
    'use-isnan': 2, //禁 止比较时使用 NaN,只能用 isNaN()
    'yield-star-spacing': [2, 'both'],
    'valid-jsdoc': 0, // jsdoc 规则
    'valid-typeof': 2, // 必须使用合法的 typeof 的值
    'vars-on-top': 2, // var 必须放在作用域顶部
    'wrap-iife': [2, 'inside'], // 立即执行函数表达式的小括号风格
    'wrap-regex': 0, // 正则表达式字面量用小括号包起来
    'yoda': [2, 'never'] // 禁止尤达条件
  }
  }
1
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
加入提交静态检测
npm i --save-dev lint-staged husky
1
向 package.json 添加运行脚本
{
  "husky": {
    "hooks": {
        "pre-commit": "lint-staged"
    }
  },
}
1
2
3
4
5
6
7
修改 package.json 配置,(和 script 同层)
"lint-staged": {
    "src/**/*.vue": [
        "eslint --fix",
        "git add"
    ],
    "src/**/*.js": [
        "eslint --fix",
        "git add"
    ]
},
1
2
3
4
5
6
7
8
9
10
{
  "husky": {
    "hooks": {
        "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/**/*.vue": [
      "eslint --fix",
      "git add"
    ],
    "src/**/*.js": [
      "eslint --fix",
      "git add"
    ]
  },
  "devDependencies": {
    "husky": "^1.2.0",
    "lint-staged": "^8.1.0",
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
自定义 ESLint 规则
var _ = require('lodash')
 
module.exports = {
  meta: {
    messages: {
      invalidName: 'Avoid use \'hello\' for identifier'
    }
  },
  create (context) {
    return {
      Identifier (node) {
        if (_.includes(node.name, 'hello')) {
          context.report({
            node,
            messageId: 'invalidName'
          })
        }
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • meta 用于定义这个规则的一些元数据(metadata)比如示例中的 messages 就用于当检测到某代码违反此规则时显示的错误信息。meta 里面还可设置更多的元数据,
  • create 方法返回一个对象,这个对象中包含着一些方法,这些方法用于在 ESLint 遍历 JS 代码的抽象语法树 (AST)时被调用。create 带有一个叫 context 的参数,它是一个对象,这个对象提供了一些非常有用的方法来帮助我们实现规则。
  • 在 crate 中的方法由两部分组成:选择器 selector 和 访问器 visitor。示例中的 identifier 便是一个选择器,简而言之,选择器就是用来过滤我们想要的语法树节点 (node)。比如我想要对所有的标识符节点做处理就用 Identifier 作为选择器。更多关于选择器的内容请参考这里。而访问器就是函数主体,也就是我们怎样定义这个规则的地方
  • ndoe.name 通过访问节点中的 name 属性来获取节点的 name。
  • context.report 这个方法用来向用户报告错误信息。其中 node 代表当前节点,messageId 引用了我们现在 meta 中的信息。
配置规则与插件

ESLint 主要依靠配置决定执行哪些规则的校验,例如我们可以通过配置no-extra-semi决定是否需要写分号,这类规则中不包含具体的业务逻辑,而是对所有项目通用,因此会被集成在 ESLint 的内置规则中。
而还有一些规则也不包含业务逻辑,但只在部分项目场景中使用,如 React 相关的大量规则,那么显然不应该集成在内置规则中,但也应该自成一个集合。这种情况下 ESLint 提供了另一种规则单位——插件,可以作为多个同类规则的集合被引入到配置中。
如果我们准备自定义一些规则用于校验项目中的业务逻辑,那么也应该创建一套自用的插件,并将自用的规则都存放其中。推荐使用 ESLint 的 yeoman generator 脚手架新建插件或规则,该脚手架能够生成插件项目的目录结构、规则文件、文档以及单元测试等模版,下文中我们将通过示例理解这些文件的的作用。

文件内局部设置
  • ESLint 可以具体文件中设置特定代码的规则,常用于跳过某条语句的检测。
  • 注销全部规则,在代码前新建一行,添加注销 /* eslint-disable */ 。如果没有恢复设置的语句,则下列全部代码都会跳过检测。
  • 恢复 ESLint ,在代码前新建一行,添加注销 /* eslint-enable */
  • 指定忽略的规则,/* eslint-disable no-alert, no-console */
  • 在特定行禁用,// eslint-disable-line
  • 在下一行禁用,// eslint-disable-next-line

Vue 编码规范

组件的命名需遵从以下原则
// 好的写法
<app-header></app-header>
 
<user-list></user-list>
 
<range-slider></range-slider>
 
<todo-item></todo-item>
 
 
// 不推荐
<!-- todo单个单词,不明确 -->
<todo></todo>
  
<!-- 虽然简短但是可读性差. 使用 `button-group` 替代 -->
<btn-group></btn-group>
  
<!-- ui 前缀太过于宽泛,在这里意义不明确 -->
<ui-slider></ui-slider>
  
<!-- 与自定义元素规范不兼容 -->
<slider></slider>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
验证组件的 props
  • 提供默认值。
  • 使用 type 属性校验类型。
  • 使用 props 之前先检查该 prop 是否存在。
<template>
  <input type="range" v-model="value" :max="max" :min="min">
</template>
<script type="text/javascript">
  export default {
    props: {
      max: {
        type: Number, // 这里添加了数字类型的校验
        default() { return 10; },
      },
      min: {
        type: Number,
        default() { return 0; },
      },
      value: {
        type: Number,
        default() { return 4; },
      },
    },
  };
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
组件结构化
<template lang="html">
    <div class="Ranger__Wrapper">
    <!-- ... -->
    </div>
</template>
<script type="text/javascript">
    export default {
        // 不要忘记了 name 属性,首字母大写
        name: 'RangeSlider',
        // 使用组件 mixins 共享通用功能
        mixins: [],
        // 组成新的组件
        extends: {},
        // 组件属性、变量
        props: {
            bar: {},
            foo: {},
            fooBar: {},
        },
        // 变量
        data() {},
        computed: {},
        // 使用其它组件
        components: {},
        // 方法
        watch: {},
        methods: {},
        // 生命周期函数
        beforeCreate() {},
        mounted() {},
    };
</script>
<style scoped>
.Ranger__Wrapper { /* ... */ }
</style>
1
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
33
34
35
避免 this.$parent
  • 通过 props 将值传递给子组件。
  • 通过 props 传递回调函数给子组件来达到调用父组件方法的目的。
  • 通过在子组件触发事件来通知父组件。
提供组件 API 文档

在模块目录中添加 README.md 文件,正式的文档会告诉开发者组件 API 变更以及向后的兼容性情况。让开发者比较容易的对组件有一个整体的认识,而不用去阅读组件的源码,也更方便开发者使用。

range-slider/
├── range-slider.vue
├── range-slider.less
└── README.md
1
2
3
4
只在需要时创建组件
  • 首先,尽可能早地尝试构建出诸如模态框、提示框、工具条、菜单、头部等这些明显的(通用型)组件。总之,你知道的这些组件以后一定会在当前页面或者是全局范围内需要。
  • 第二,在每一个新的开发项目中,对于一整个页面或者其中的一部分,在进行开发前先尝试思考一下。如果你认为它有一部分应该是一个组件,那么就创建它吧。
  • 最后,如果你不确定,那就不要。避免那些“以后可能会有用”的组件污染你的项目。它们可能会永远的只是(静静地)待在那里,这一点也不聪明。注意,一旦你意识到应该这么做,最好是就把它打破,以避免与项目的其他部分构成兼容性和复杂性。
尽可能使用 mixins ,封装可重用的代码,共享有相同的功能
const MenuMixin = {
    data () {
        return {
            language: 'EN'
        }
    },
    methods: {
        changeLanguage () {
            if (this.language === 'DE') {
                this.$set(this, 'language', 'EN')
            }
            if (this.language === 'EN'){
                this.$set(this, 'language', 'DE')
            }
        }
    }
}
export default MenuMixin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
单文件组件文件的大小写
  • 单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。
  • 应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。
  • 只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。
  • 和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
  • 组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。
  • 组件名应该倾向于完整单词而不是缩写。
Prop 名大小写

在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case。

注释

文件注释

文件注释位于文件的最前面,应包括文件的以下信息:概要说明及版本(必须)项目地址(开源组件必须)版权声明(必须)开源协议(开源组件必须)版本号(必须)修改时间(必须),以ISO格式表示(可使用Sublime Text的InsertDate插件插入)文件注释必须全部以英文字符表示,并存在于文件的开发版本与生产版本中。例如:

/*!
 * jRaiser 2 Javascript Library
 * waterfall - v1.0.0 (2013-03-15T14:55:51+0800)
 * http://jraiser.org/ | Released under MIT license
 */
1
2
3
4
5
/*!
 * kan.56.com - v1.1 (2013-03-08T15:30:32+0800)
 * Copyright 2005-2013 56.com
 */
1
2
3
4

如果文件内包含了一些开源组件,则必须在文件注释中进行说明。例如:

/*!
 * jRaiser 2 Javascript Library
 * sizzle - v1.9.1 (2013-03-15T10:07:24+0800)
 * http://jraiser.org/ | Released under MIT license
 *  * Include sizzle (http://sizzlejs.com/)
 */ 
1
2
3
4
5
6
普通注释

普通注释是为了帮助开发者和阅读者更好地理解程序,不会出现在API文档中。其中,单行注释以“//”开头;多行注释以“/”开头,以“/”结束。普通注释的使用需遵循以下规定。

  • 总是在单行注释符后留一个空格。例如:
// this is comment
1

总是在多行注释的结束符前留一个空格(使星号对齐),不要把注释写在多行注释的开始符、结束符所在行。例如:

/*
here is line 1
here is line 2
 */
1
2
3
4
  • 如果某段代码有功能未实现,或者有待完善,必须添加“TODO”标记,“TODO”前后应留一个空格。例如:
// TODO 未处理IE6-8的兼容性
function setOpacity(node, val) {
    node.style.opacity = val;
}
1
2
3
4
文档注释

文档注释将会以预定格式出现在API文档中。它以“/**”开头,以“/”结束,其间的每一行均以“”开头(均与开始符的第一个“”对齐),且注释内容与“”间留一个空格。例如:

/**
 * comment
 */
1
2
3

文档注释必须包含一个或多个注释标签。

  • @module。声明模块,用法:
/**
 * 模块说明
 * @module 模块名
 */
1
2
3
4
/**
 * Core模块提供最基础、最核心的接口
 * @module Core
 */
1
2
3
4
@class。声明类,用法:
/**
 * 类说明
 * @class 类名
 * @constructor
 */
1
2
3
4
5

@class必须搭配@constructor或@static使用,分别标记非静态类与静态类。

/**
 * 节点集合类
 * @class NodeList
 * @constructor
 * @param {ArrayLike<Element>} nodes 初始化节点
 */
1
2
3
4
5
6
  • @method。声明函数或类方法,用法:
/**
 * 方法说明
 * @method 方法名
 * @for 所属类名
 * @param {参数类型} 参数名 参数说明
 * @return {返回值类型} 返回值说明
 */
1
2
3
4
5
6
7

没有指定@for时,表示此函数为全局或模块顶层函数。当函数为静态函数时,必须添加@static;当函数有参数时,必须使用@param;当函数有返回值时,必须使用@return。例如:

/**
 * 返回当前集合中指定位置的元素
 * @method
 * @for NodeList
 * @param {Number} [i=0] 位置下标。如果为负数,则从集合的最后一个元素开始倒数
 * @return {Element} 指定元素
 */
1
2
3
4
5
6
7
  • @param。声明函数参数,必须与@method搭配使用。
  • 当参数出现以下情况时,使用对应的格式:
[参数名]
1
  • 参数有默认值:
[参数名=默认值]
1

@property。声明类属性,用法:

/**
 * 属性说明
 * @property {属性类型} 属性名
 */
1
2
3
4
Last Updated: 2019-8-30 6:26:45 PM