Vue 中的风格统一工具
eslint / prettier
是什么,有何区别:https://zhuanlan.zhihu.com/p/80574300
.eslintrc.js
ESLint
支持几种格式的配置文件,如果同一个目录下有多个配置文件,ESLint
只会使用一个。优先级顺序如下
JavaScript
- 使用 .eslintrc.js 然后输出一个配置对象YAML - 使用
.eslintrc.yaml 或 .eslintrc.yml 去定义配置的结构JSON -使用
.eslintrc.json 去定义配置的结构,ESLint 的 JSON 文件允许 JavaScript 风格的注释Deprecated
-使用 .eslintrc,可以使 JSON 也可以是 YAMLpackage.json
在 package.json 里创建一个 eslintConfig 属性,在那里定义你的配置
代码规范
推荐项目目录结构
dist
css
doc
src
...
.editorconfig
.travis.yml
README.md
.editorconfig
每个项目应当「SHOULD」包含 .editorconfig 用来统一配置编辑器的换行、缩进存储格式
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
Readme
每个项目都必须「MUST」包含一个README.md文件,此文件中应当「SHOULD」概要描述此项目的功能和特点等信息
eslint + prettierrc
- .eslintrc.js
module.exports = {
root: true,
env: {
node: true,
},
extends: ['plugin:vue/essential', '@vue/prettier'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'comma-dangle': ['error', 'always-multiline'],
quotes: [
'error',
'single',
{
avoidEscape: true,
},
],
curly: ['error', 'all'],
},
parserOptions: {
parser: 'babel-eslint',
},
};
- .eslintignore
# eslintIgnore files
src/api/indexApi.js
- .prettierrc.js ? prettier.config.js 到底哪个目前未知
module.exports = {
trailingComma: 'all',
singleQuote: true,
arrowParens: 'always',
endOfLine: 'lf',
};
js 代码风格
- 在文件结尾处,保留一个空行
- 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格
- 代码块左花括号 { 前必须有一个空格
- [强制] if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格
- 对象属性 key: value 中,: 后必须有空格,: 前不允许有空格
- 函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格,)与{之间空格
- [强制] , 和 ; 前不允许有空格。
- [强制] 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格
- [强制] 行尾不得有多余的空格, 每行不得超过 120 个字符
- 运算符处换行时,运算符必须在新行的行首
- [建议] 不同行为或逻辑的语句集,使用回车隔开,更易阅读
- 对于 if…else…、try…catch…finally 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好
- [强制] 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {…}
a++;
a = b + c;
if ('代码规范') {
console.log('那你真棒');
} // 无分号
var obj = {
key: value, // 尾逗号
};
function isAGoodMan() {
return true;
}
callFunc(param1, this.obj[index += (count--)], param3);
if (a > b) {
some code...
}
// good
if (user.isAuthenticated()
&& user.isInRole('admin')
&& user.hasAuthority('add-admin')
|| user.hasAuthority('delete-admin')
) {
// Code
}
var result = number1 + number2 + number3
+ number4 + number5;
if (true) {
}
else if (false) {
}
else {
}
- [强制] 函数定义结束不允许添加分号,函数表达式,分号是不允许省略
- if 结尾没分号
function a() {
}
var b = function () {
};
命名
- [强制] 常量:全部字母大写,单词间下划线分隔
- [强制] 类名 使用 名词,大驼峰
- [强制] 枚举变量 使用 Pascal 命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。
- [强制] 命名空间 使用 Camel命名法
- [建议] 函数名 使用 动宾短语
- [建议] boolean 类型的变量使用 is 或 has 开头
- [建议] Promise 对象 用 动宾短语的进行时 表达。
var MY_LOVE = '//www.4399.com';
class Person(name, age) {
};
var TargetState = {
READING: 1,
READED: 2,
APPLIED: 3,
READY: 4,
};
var isReady = true;
function getUserInfo() {
};
var loadingData = ajax.get('url');
loadingData.then(callback);
- 多用
let const
,少用var
const > let
js 编译器加持- 字符串静态用
' '
动态用``
- 数组/对象成员对变量赋值时,优先使用解构赋值
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
}
// best
function getFullName({ firstName, lastName }) {
}
- 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
const obj = {name:'zhangsan'}
const obj = {
name: 'zhangsan',
}
- 箭头函数及时一个参数也要()
- class 代替 构造函数
CSS
CSS 引号
属性选择器和属性值用单引号,URI 的值不需要引号 (对单引号保持中立)
@import url(//www.google.com/css/maia.css);
html {
font-family: 'open sans', arial, sans-serif;
}
注释
- [强制] 必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致
- [建议] 避免使用 /…/ 这样的多行注释。有多行注释内容时,使用多个单行注释
- [强制] 文档注释前必须空一行。
- [建议] 自文档化的文档说明 what,而不是 how。
类型注释
- [强制] 类型定义都是以{开始, 以}结束。常用类型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}
- [强制] 对于基本类型 {string}, {number}, {boolean},首字母必须小写
类型定义 | 语法示例 | 解释 |
String | {string} | — |
Number | {number} | — |
Boolean | {boolean} | — |
Object | {Object} | — |
Function | {Function} | — |
RegExp | {RegExp} | — |
Array | {Array} | — |
Date | {Date} | — |
单一类型集合 | {Array.<string>} | string 类型的数组 |
多类型 | {(number|boolean)} | 可能是 number 类型, 也可能是 boolean类型 |
允许为null | {?number} | 可能是 number, 也可能是 null |
不允许为null | {!Object} | Object 类型, 但不是 null |
Function类型 | {function(number, boolean)} | 函数,形参类型 |
Function带返回值 | {function(number, boolean):string} | 函数, 形参, 返回值类型 |
参数可选 | @param {string=} name | 可选参数, =为类型后缀 |
可变参数 | @param {…number} args | 变长参数, …为类型前缀 |
任意类型 | {*} | 任意类型 |
可选任意类型 | @param {*=} name | 可选参数,类型不限 |
可变任意类型 | @param {…*} args | 变长参数,类型不限 |
/** 数据发送 ex:sendMessage({event,params})
* @param {string} event 事件名
* @param {Array|all} params 事件入参 根据数组顺序入参
*/
文档注释
- [强制] 文件顶部必须包含文件注释,用 @file 标识文件说明
- [建议] 文件注释中可以用 @author 标识开发者信息
开发者信息能够体现开发人员对文件的贡献,并且能够让遇到问题或希望了解相关信息的人找到维护人。通常情况文件在被创建时标识的是创建者。随着项目的进展,越来越多的人加入,参与这个文件的开发,新的作者应该被加入 @author 标识。
@author 标识具有多人时,原则是按照 责任 进行排序。通常的说就是如果有问题,就是找第一个人应该比找第二个人有效。比如文件的创建者由于各种原因,模块移交给了其他人或其他团队,后来因为新增需求,其他人在新增代码时,添加 @author 标识应该把自己的名字添加在创建人的前面。
@author 中的名字不允许被删除。任何劳动成果都应该被尊重。
业务项目中,一个文件可能被多人频繁修改,并且每个人的维护时间都可能不会很长,不建议为文件增加 @author 标识。通过版本控制系统追踪变更,按业务逻辑单元确定模块的维护责任人,通过文档与wiki跟踪和查询,是更好的责任管理方式。
对于业务逻辑无关的技术型基础项目,特别是开源的公共项目,应使用 @author 标识
/**
* @file Describe the file
* @author
* author-name(mail-name@domain.com)
* author-name2(mail-name2@domain.com)
*/
/**
* @file Describe the file
* @author author-name(mail-name@domain.com)
* author-name2(mail-name2@domain.com)
*/
类注释
- [建议] 使用 @class 标记类或构造函数
- 使用 @constructor 来标记构造函数
- 使用 @extends 标记类的继承信息
- [强制] 类的属性或方法等成员信息使用 @public / @protected / @private 中的任意一个,指明可访问性。
/**
* 类描述
*
* @class
* @extends Developer
*/
var Fronteer = function() {
Developer.call(this);
/**
* 属性描述
*
* @type {string}
* @private
*/
this._level = 'T12';
// constructor body
};
util.inherits(Fronteer, Developer);
/**
* 方法描述
*
* @private
* @return {string} 返回值描述
*/
Fronteer.prototype._getLevel = function() {};
函数/方法注释
- [强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识
- [强制] 参数和返回值注释必须包含类型信息和说明。
- [建议] 当函数是内部函数,外部不可访问时,可以使用 @inner 标识。
/**
* 函数描述
*
* @param {Object} eventList 参数描述
* @return {Function} 返回值描述
*/
function addMethods(eventList) {
return function(methodName, fnc) {
if (fnc instanceof Function) {
eventList[methodName] = fnc;
}
};
}
事件注释
- [强制] 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同
/**
* 事件描述 - 值变更时触发
*
* @event
* @param {Object} e e描述
* @param {string} e.before before描述
* @param {string} e.after after描述
*/
onchange: function (e) {
}
常量注释
- [强制] 常量必须使用 @const 标记,并包含说明和类型信息
/**
* 常量说明
*
* @type {string}
*/
const REQUEST_URL = 'myurl.do';
细节注释
- 对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。
- [建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。
- [强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。
一些常用标记:
TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
XXX: 该处存在陷阱。此时需要对陷阱进行描述。
function fn() {
// 这里是细节说明
// HACK 此处没问题,但是有问题
// FIXME 赶时间写死数
}
多行注
快捷键:shift + options + a
@param @argument 指定参数名和说明来描述一个函数参数
@returns 描述函数的返回值
@author 指示代码的作者
@deprecated 指示一个函数已经废弃,而且在将来的代码版本中将彻底删除。要避免使用这段代码
@see 创建一个HTML链接,指向指定类的描述
@version 指定发布版本
@requires 创建一个HTML链接,指向这个类所需的指定类
@throws @exception 描述函数可能抛出的异常的类型
{@link} 创建一个HTML链接,指向指定的类。这与@see很类似,但{@link}能嵌在注释文本中
@fileoverview 这是一个特殊的标记。如果在文件的第一个文档块中使用这个标记,则指定该文档块的余下部分将用来提供这个文件的概述
@class 提供类的有关信息,用在构造函数的文档中
@constructor 明确一个函数是某个类的构造函数
@type 指定函数的返回类型
@extends 指示一个类派生了另一个类。JSDoc通常自己就可以检测出这种信息,不过,在某些情况下则必须使用这个标记
@private 指示一个类或函数是私有的。私有类和函数不会出现在HTML文档中,除非运行JSDoc时提供了–private命令行选项
@final 指示一个值是常量值。要记住JavaScript无法真正保证一个值是常量
@ignore JSDoc忽略有这个标记的函数
/**
* @fileOverview 简单的类对象标注示例
* @author <a href=”llying.javaeye.com”>llying</a>
* @version 0.1
*/
/** 方法说明
* @author llying
* @constructor Person
* @description 一个Person类
* @see The <a href=”#”>llying</a >.
* @example new Parent(“张三”,15);
* @since version 0.1
* @param {String} username 姓名
* @param {Num} age 年龄
*/
function Person(username,age)
{
/**
* @description {Sting} 姓名
* @field
*/
this.username = username;
/**
* @description {Num} 年龄
* @field
*/
this.age = age;
/**
* @description 弹出say内容
* @param {String} content 内容
*/
this.say = function(content)
{
alert(this.username+” say :”+content);
}
/**
* @description 返回json格式的对象
* @return {String} json格式
* @see Person#say
*/
this.getJson = function(){
return “{name:”+this.username+”,age”+this.age+”}”;
}
}