HTML 规范
- 文档类型声明及编码:统一为 HTML5 声明类型标准。书写时利用 IDE 实现层次分明的缩进(默认缩进 2 空格)。
- 指定页面编码为 utf-8,减少 ASCII 码出现的中文乱码情况。
- 根据 HTML 元素的语义使用相关元素。
- 非特殊情况下 CSS 文件放在 body 部分<head>标签后。非特殊情况下大部分 JS 文件放在<body>标签尾部(如果需要界面未加载前执行的代码可以放在 head 标签后)避免行内 JS 和 CSS 代码。
- 所有编码需要遵循 html(XML)标准,标签&属性&属性命名必须由小写字母及中划线数字组成,且所有标签必须闭合,包括 br(),hr()等。属性值用双引号。
- 需要为 html 元素添加自定义属性的时候,首先要考虑下有没有默认的已有的合适标签去设置,如果没有,可以使用须以"data-"为前缀来添加自定义属性,避免使用"data:"等其他混乱命名方式。
- 语义化 html,如标题根据重要性用 h*(同一页面只能有一个 h1),段落标记用 p,列表用 ul,内联元素中不可嵌套块级元素。
- 尽可能减少不必要的 div 多层级嵌套,节点的优化是必要的。
- 书写链接地址时,必须避免重定向,例如:href="
http://myVue.com/
",即须在 URL 地址后面加上“/”。 - 添加合理的注释,说明相关代码是做什么的,只在关键代码处添加相关注释,避免过多添加增加 HTML 和 CSS 的代码量。
- 使用 TODO 注释,说明代码功能。
Vue 框架规范
- 版本要求:Vue 版本不低于 3.2。构建工具 Vite 版本不低于 2.9。状态管理工具 Pinia 版本不低于 2.0。UI 库推荐 Element Plus,和大部分已有项目的 elementUi 升级相对平滑,版本尽可能使用最新版本。
- 技术栈
vue全家桶(Vue3)、ts/ES6、tslint/eslint、less、组件库:PC:element-ui,移动端:vant
开发相关:WebStorm/VS code、git、pxcook(账号使用中文名)、Chrome/firefox
注意
原则上,所有项目都必须使用 Typescript
语言。
除此之外,Vue3
指定最低版本为3.2
,React
指定最低版本为18.1.0
。请及时更新你的技术栈。
- 项目结构
这只是项目模板中的基础结构,具体需要结合项目需求进行定制化修改;以下项目结构基本上可以满足项目开发。
- 整体结构
|- /api // 接口请求Api层
|- /assets // 静态资源文件
|- /base // BLL业务逻辑层基类(构造函数)
|- /components // 公共组件
|- /utils // 工具箱帮助
|- /plugins // 插件
|- /types // 类型定义文件
|- /directives // 自定义指令
|- /router // 路由文件
|- /styles // 公用样式
|- /views // 业务目录
|- /store // 状态管理
- views层结构规范
views层是表现层,为了避免单个文件代码量过大的隐患,Vue组件采用 template、styles、js 分离的形式,外层采用业务文件夹命名的方式。结构清晰、单个文件的代码量较少、后期易于维护、特殊组件/文件置于业务文件夹。
分离规则:js>200行 与 css>100行 将js & css 单独抽出
|- /views
|- /home-page
|- index.vue
|- index.ts
|- index.less
|- ...
- 文件命名
文件夹命名、文件命名、组件命名
命名规则:kebab-case命名规范(短横线命名)
不能含有特殊字符、空格
文件名应该更倾向于完整单词而不是缩写
vue文件命名:统一使用小写字母开头的(kebab-case)命名规范;非vue文件(ts、js、css、less……)
组件必须包含 name 属性
全局通用的组件放在/src/components下
应该以一个特性的前缀开头,比如 v
组件名称 name: 使用大写字母开头的 PascalBase 风格
|- /src
|- /components
|- /v-table
|- index.vue
|- /v-svg-icon
|- index.vue
|- ...
其它业务页面中的组件,未成为跨频道的非公共集合组件。放在个子页面下的 ./components文件夹下
|- /home-page
|- /components
|- pan-item.vue
|- index.vue
|- index.ts
|- ...
函数、变量、常量、css类名、标签命名
命名使用英文,不允许出现拼音、拼音首字母等命名,要求通俗易懂
- 函数、变量
命名规则:小驼峰
且变量定义方式必须使用 let,不允许使用 var
let tempName = ' '
function initData() {
}
- 常量
命名规则:全部大写:使用大写字母和下划线组合命名,下划线用于分隔单词
此规则适用于位于文件顶部常量、独立的常量文件中的命名,如果是方法体内部的常量,应当使用小驼峰的命名规则
const CASE_ID = 'XXX'
- css类名
命名规则:class为完整小写单词并以 - 连接
.form-input {
font-size: 14px;
color: #333333;
}
尽量不要使用内嵌式style
注:ID的权重高,一般情况下,不应用于样式;如果需要使用 ID 解决样式的优先级,ID的命名规则为:帕斯卡命名法
- 标签
非 Html 标签,比如:组件名(参照文件命名:kebab-case命名规范),则在template中,组件引用使用小写单词且用 - 连接
<template>
<i-table></i-table>
</template>
<script>
import iTable from '@/components/iTable/index.vue'
export default {
components: { ITable },
...
}
</script>
注释
项目中使用到的注释一般为:文件头注释、html块级注释、多行注释、单行注释、函数注释;且完整项目中的代码注释量不能少于代码总量的 30%
。
- 单行注释
单独一行:// (双斜杠)与注释内容之间保留一个空格
在代码后添加注释:// (双斜杠)与代码之间保留一个空格,同时与注释内容之间保留一个空格
let userName = '' // 登录名
- 多行注释
以 /* 开头,以 */ 结尾,且与注释内容之间保留一个空格
/* this.tableCurrentPagination = {
pageIndex: 1,
pageSize: size
} */
- 函数注释
函数注释也是多行注释的一种
/**
* 取消收藏
*/
- html块级注释
可以将 html dom 按照功能块级折叠,方便阅读和维护
<!--region 选择框-->
<el-table-column v-if="options.mutiSelect" type="selection" style="width: 55px;">
</el-table-column>
<!--endregion-->
注:webstorm已经配置了注释模板,输入 re 点击 Tab即可
- 文件自动生成注释头信息
最新版本参考主目录里面的注释规范
其它
尽量使用缩写属性
尽量使用缩写属性对于代码效率和可读性是很有用的
// bad
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
// good
border-top: 0;
padding: 0 1em 2em;
0 后面不带单位
// bad
padding-bottom: 0px;
margin: 0em;
// good
padding-bottom: 0;
margin: 0;
Prop
Prop定义应该尽量详细,必须指定其类型
props: {
total: {
type: Number,
default: 0
}, // 总数
pagination: {
type: Object,
default: null // 分页参数 === pageSize:每页展示的条数,pageIndex:当前页,pageArray: 每页展示条数的控制集合,默认 _page_array
}
}
为 v-for 设置键值
在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态
<ul>
<li v-for="todo in todos"
:key="todo.id">
{{ todo.text }}
</li>
</ul>
避免 v-if 和 v-for 用在一起
永远不要把 v-if 和 v-for 同时用在同一个元素上
<ul v-if="shouldShowUsers">
<li v-for="user in users"
:key="user.id">
{{ user.name }}
</li>
</ul>
为组件样式设置作用域
对于用于来说,顶级App组件和布局组件的样式可以是全局的,但是其它所有组件都应该是有作用域的。这条规则只和单文件组件有关。你不一定要使用 scoped 特性。
<template>
<button class="button button-close">X</button>
</template>
<!-- 使用 `scoped` 特性 -->
<style scoped>
.button {
border: none;
border-radius: 2px;
}
.button-close {
background-color: red;
}
</style>
使用 TODO 标注问题的解决方式
vue文件中的模块顺序
<script></script>
<template></template>
<style></style>
vue文件中的指令缩写
指令缩写 (用 : 表示 v-bind: 和用 @ 表示 v-on:)
跨域
跨域线下通过proxytable解决,线上通过运维配置Nginx代理
状态管理
如果需要缓存和持久化,使用 pinia-plugin-persist
package项目依赖
dependencies
和devDependencies
遵循一个原则,非必要和依赖的,都要求安装到devDependencies
目录里,即通过--save-dev
的方式安装。
可以保证在production正式环境下,一些非依赖项不会影响项目的打包,还可以减少每次build相关的必要静态资源的体积大小。
调试
避免线上环境出现控制台输出、警告、报错等信息
推荐使用console.debug
其他补充
参数要声明类型,尽量不要使用any
单组件类名与文件名保持一致
CSS 规范
- 协作开发及分工:项目主负责人会根据各个模块,同时根据页面相似程序,事先写框架文件,分配给前端人员实现内部结构&表现&行为。共用 css 文件由主负责人书写,协作开发过程中,每个页面请务必都要引入。此文件包含 reset 默认样式、初始化样式及公共头部底部样式,此文件不可随意修改。
- class 与 id 的使用:id 是唯一的并是父级的,class 是可以重复的并是子级的,所以 id 仅使用在主模块或者需要调用的 dom 上,class 可用在重复使用率高及子级中。另一部分 id 命名为 JS 预留钩子的,开发过程中开发人员自行添加。
- 为 JS 预留钩子的命名,请以 j-起始,比如:j-hide,j-show。
- class 与 id 命名:大的框架命名比如 header/footer/wrapper/left/right 之类的在项目中由主负责人统一命名。其他样式名称由小写英文、中划线、数字来组合命名,如 i-comment,fontred,w200。尽量避免使用中文拼音,使用简易的单词组合。总之,命名要语义化,简明化。另附一份参考命名表
- css 属性书写顺序,建议遵循:布局定位属性-->自身属性-->文本属性-->其他属性.此条可根据自身习惯书写,但尽量保证同类属性写在一起.属性列举:
- 布局定位属性主要包括:display&list-style&position(相应的 top,right,bottom,left)&float&clear&visibility&overflow;
- 自身属性主要包括:width&height&margin&padding&border&background。
- 文本属性主要包括:color&font&text-decoration&text-align&vertical-align&white-space
- 其他&content。
- 书写代码前,合理规划提高样式重复使用率。
- 充分利用 html 自身属性及样式继承原理减少代码量。
- 背景图片请尽可能使用精灵图技术,减小 http 请求,考虑到多人协作开发,精灵图按模块制作。
- 基于新技术和移动端的业务,可以使用 iconfont 和 svg 来替代一些纯色图片,实现更灵活弹性的效果。
- 用 png 图片做图片时,原则上要求图片格式为 png8 格式,若 png8 实在影响图片质量或其中有半透明效果,再使用 png24,目的是尽可能的减少图片的体积。
- 推荐相对的减少使用影响性能的属性,比如 position,float。
- 必须为大区块样式添加注释,小区块适量注释。
- 代码缩进与格式:建议一行一个属性书写,方便注释调试效果。
- 在页面中尽量避免使用 style 属性,即 style="…"。
- 能以背景形式呈现的图片,尽量写入 css 样式中。
更多详情规则和建议指南
格式
使用 2 个空格作为缩进
类名使用中划线作为标准
不要使用 ID 选择器
在一个规则声明中应用了多个选择器时,每个选择器独占一行
标签,属性,属性值,css 选择器,属性,和属性值只使用小写
在规则声明的左大括号 { 前加上一个空格;在属性的冒号 : 后面加上一个空格,前面不加空格;规则声明的右大括号 } 独占一行;规则声明之间用空行分隔开。
注释
内容部分建议使用行注释 (在 less 中是 //) 代替块注释。
建议注释独占一行。避免行末注释。
给没有自注释的代码写上详细说明,比如:
- // 为什么用到了 z-index
- // 兼容性处理或者针对特定浏览器的 hack
不建议使用 ID 选择器。ID 选择器会带来不必要的优先级,而且 ID 选择器是不可复用的
JavaScript 钩子。避免在 Css 和 JavaScript 中绑定相同的类,不利于后续维护,因为写定后就相当于样式和 js 文件绑定了,改样式名词会造成页面逻辑出错。如果要涉及到与样式有关的操作,添加.js-前缀
定义边框无样式时,使用 0 代替 none
不要让嵌套选择器深度超过 3 层
CSS 规则
将行为和表现分开。严格遵循结构,表现和行为的分离,尽量保证三者交互保持最低,确保所有表现都放到样式表中,所有行为都放到脚本中,要尽量减少外链
引用属性值时,使用双引号
对 class 命名时要能保证光从名字就能看出是干什么用的,命名也要简短易懂
避免使用 CSS 类型选择器
// bad
div.error {}
// good
.error {}
- 写属性值时尽量使用缩写
// bad
padding-bootom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
...
// good
padding: 0 1em 2em;
- 除非必要,否则省略 0 后面的单位
flex: 0px;
flex: 1 1 0px; /* IE11下会要求添加单位 */
- 省略 0 开头小数前面的 0
// bad
font-size: 0.8em;
// good
font-size: .8em;
- 十六进制尽可能使用 3 个字符
// bad
color: #eebbcc;
// good
color: #ebc;
- 用-连接命名,增进对名字的理解和查找
// bad
.demoimage {}
.error_status {}
// good
.video-id {}
避免使用 css hacks
选择器在大括号前要添加一个空格
// bad
.test{
...
}
// good
.test {
...
}
- 在每个声明尾部都加上分号
// bad
.test {
display: block;
height: 100px
}
// good
.test {
display: block;
height: 100px;
}
- 在属性名冒号结束后添加一个空格
// bad
h3 {
font-size:16px;
}
// good
h3 {
font-size: 16px;
}
- 当有多个选择器时,每个选择器都要独立新行
// bad
h1, h2, h3 {
}
// good
h1,
h2,
h3 {
}
- 多个规则之间要隔行
// bad
html {
...
}
body {
...
}
// good
html {
...
}
body {
...
}
- 按模块功能写注释
JS 书写规范
基础约定
- 书写过程过,每行代码结束必须有分号。
- 库引入:原则上仅引入共识的 JS 库,如 jQuery 库,若需引入第三方库,须与团队其他人员讨论决定。
- 变量命名:驼峰式命名.原生 JS 变量要求是纯英文字母,首字母须小写,如 myVue。另要求变量集中声明,避免全局变量。
- 类命名:首字母大写,驼峰式命名.如 MyVue。
- 函数命名:首字母小写驼峰式命名.如 myVue()。
- 命名语义化,尽可能利用英文单词或其缩写。
- 尽量避免使用存在兼容性及消耗资源的方法或属性,比如 eval_r()&innerText。
- 代码结构明了,加适量注释。注意提高函数重用率。
- 注重与 html 分离,减小 reflow,注重浏览器性能。
- 变量
总是使用 const 或 let 声明变量,避免全局变量污染
每个变量在声明时,都要使用 const 或 let
// bad
const items = "rede",
goSportsTeam = true,
dragonball = "z";
export { items, goSportsTeam, dragonball };
// good
const items = "rede";
const goSportsTeam = true;
const dragonball = "z";
export { items, goSportsTeam, dragonball };
- 将所有的 const 和 let 的定义分组
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
- 在需要的地方再对变量进行分配
// bad
// 这里的checkName中name会调用getName函数,但是name并不是总会返回,如果hasName为test时,并不会返回,所以,应该把对name的定义延后
const getName = function backName() {
return "rede";
};
function checkName(hasName) {
const name = getName();
if (hasName === "test") {
return false;
}
return name;
}
// good
const getName = function backName() {
return "rede";
};
function checkName(hasName) {
if (hasName === "test") {
return false;
}
const name = getName();
return name;
}
- 禁止使用连续赋值
// bad
const a = (b = c = 1);
// good
const a = 1;
const b = a;
const c = a;
- 避免使用++或--运算符
// bad
let num = 1;
num++;
--num;
// good
let num = 1;
num += 1;
num -= 1;
- 在=号前后要避免进行换行,如果变量名超过最长限制,要统一换行方式
// bad
// 此时的换行是没有必要的
const foo = "superLongLongLongLongLongLongLongLongString";
// good
const foo = "superLongLongLongLongLongLongLongLongString";
// bad
const foo = superLongLongLongLongLongLongLongLongFunctionName();
// good
const foo = superLongLongLongLongLongLongLongLongFunctionName();
- 变量提升
- 引用
- 对变量的声明不要使用 var,如果是声明后不再重现分配的变量要使用 const,如果是重现分配的变量要使用 let。 注意 let 和 const 的块级作用域
- 对象
- 使用对象直接量创建对象
const item = new Object(); //bad
const item = {}; //good
- 如果一个对象的 key 值包含动态属性,要保证对象的所有属性都是在一个地方进行定义
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 atom = {
value: 1,
addValue: function(value) {
return atom.value + value;
}
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
}
};
- 在定义对象属性时,如果 key 值和 value 值同名,要使用简写形式
const lukeSkywalker = "Luke Skywalker";
// bad
const obj = {
lukeSkywalker: lukeSkywalker
};
// good
const obj = {
lukeSkywalker
};
- 在定义对象时,简写的属性和非简写的属性要按顺序写,不要混着写
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
};
- 在定义对象时,key 值只有在无效识别时添加单引号
// bad
const bad = {
foo: 3,
bar: 4,
"data-blah": 5
};
// good
const good = {
foo: 3,
bar: 4,
"data-blah": 5
};
- 不允许直接使用 Object.prototype 的相关方法,比如 hasOwnProperty, propertyIsEnumerable, 和 isPrototypeOf.因为在定义对象时,有可能会覆盖了这些方法
const item = {
name: "rede"
};
// bad
console.log(item.hasOwnProperty("name"));
// good
console.log(Object.prototype.hasOwnProperty.call(item, "name"));
// best
const has = Object.prototype.hasOwnProperty;
console.log(has.call(item, "name"));
- 使用对象展开符浅复制对象,不要使用 Object.assign
// very bad
// 本意是复制一个对象,在从复制出的对象上删除某个值,但实际上原始对象的值也会被影响
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 });
delete copy.a;
// bad
// 可以达到预期目的,但在写法上可读性不好
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
delete copy.a;
// 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 }
- 数组
- 在定义数组时,使用数组直接量,不要使用 new Array 的形式
// bad
const items = new Array();
// good
const items = [];
- 在向数组添加元素时,不要直接赋值,通过 push 添加(注意指的是添加,并不包括修改的情况)
const items = [];
// bad
items[items.length] = "test";
// good
items.push("test");
- 复制数组时,使用展开操作符
const items = [1, 2, 4, 8, 16];
// bad
const itemsCopy = [];
for (let i = 0; i < items.length; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
- 在把类数组对象转为数组对象时,使用展开操作符来替代 Array.from
const foo = document.querySelectorAll(".foo");
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
- 使用 Array.from 代替展开操作符来处理针对数组的映射操作,可以避免操作创建中间数组
const foo = [1, 2, 3];
// bad
const baz = [...foo].map(x => x + x);
// good
const baz = Array.from(foo, x => x + x);
- 解构
- 优先使用对象解构来访问对象属性
// 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}`;
}
// best
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;
- 用对象解构去处理多个返回值
const input = {
left: "0px",
right: "0px",
top: "0px",
bottom: "0px"
};
// bad
function processInput(input) {
const left = input.left;
const right = input.right;
const top = input.top;
const bottom = input.bottom;
return [left, right, top, bottom];
}
const [left, __, top] = processInput(input);
// good
function processInput({ left, right, top, bottom }) {
return {
left,
right,
top,
bottom
};
}
const { left, top } = processInput(input);
- 字符串
- 字符串使用单引号
// bad
const name = "Capt. Janeway";
// bad
const name = `Capt. Janeway`;
// good
const name = "Capt. Janeway";
- 当字符串过长时,不要使用字符串连接符写成多行
// bad
// 此时报的检查错误是no-multi-str,保证字符串不分两行书写
const errorMessage =
"This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.";
// bad
// 此时运行代码检查并不会报错,针对这种写法应该是建议不要这么写
const errorMessage =
"This is a super long error that was thrown because " +
"of Batman. When you stop to think about how Batman had anything to do " +
"with this, you would get nowhere fast.";
// good
// 要是感觉代码过长不方便看时,可以在编译器上做相关设置,设置自动换行
const errorMessage =
"This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.";
- 当字符串和变量要进行连接时,要使用模版字面量
// bad
function sayHi(name) {
return "How are you, " + name + "?";
}
// bad
// 检查的错误template-curly-spacing,禁止模版字符串前后使用空格
function sayHi(name) {
return `How are you, ${name}?`;
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
针对字符串不要使用 eval
在字符串中只在有意义的地方使用转义字符
// bad
const foo = "'this' is \"quoted\"";
// good
const foo = "'this' is \"quoted\"";
- 函数
- 在定义函数时,使用指定名词的函数表达式,而不是匿名表达式
// bad
function add(a, b) {
return a + b;
}
// bad
const add = function(a, b) {
return a + b;
};
// good
const add = function myAdd(a, b) {
return a + b;
};
- 立即执行函数要用大括号包裹起来
// bad
const x = (function() {
return { y: 1 };
})();
// not bad
// 随着现在模块的普及立即执行函数使用的应该不多了
const x = (function() {
return { y: 1 };
})();
禁止循环中出现函数,如果需要在循环中定义函数,最好把相关函数使用变量定义好再使用,避免形成闭包
禁止在代码块中使用函数声明,不过可以使用函数表达式
// bad
const currentUser = true;
if (currentUser) {
function test() {
console.log("Nope.");
}
}
// good
const currentUser = true;
let test;
if (currentUser) {
test = () => {
console.log("Nope.");
};
}
- 函数接收的形参不可以使用 argumenst
// bad
function foo(name, options, arguments) {
// ...
}
- 使用默认参数语法,不要去使用存在变化的函数参数
// bad
// 这个会报no-param-reassign,禁止对 function 的参数进行重新赋值
function handleThings(opts) {
opts = opts || {};
...
}
// bad
// 报的错误同上
function handleThings(opts) {
if (opts === 0) {
opts = {};
}
}
// good
function handleThings(opts = {}) {
// ...
}
- 避免对函数默认参数进行不可预期的操作
// bad
// 检查时会报no-plusplus,禁用++
// 这个例子应该是要说明,不对默认参数设置不确定的因素,就比如这里的b值,两次调用返回值都不同,会令人费解
let b = 1;
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
- 始终将默认参数,放到最后
// bad
function handleThings(opts = {}, name) {
return { opts, name };
}
// good
function handleThings(name, opts = {}) {
return { opts, name };
}
- 禁用 Function 构造函数创建函数
// bad
const x = new Function("a", "b", "return a + b");
// good
const x = function backAdd(a, b) {
return a + b;
};
- 强制在函数圆括号前和代码块之前使用统一的空格
// bad
const f = function() {};
const g = function() {};
const h = function() {};
// good
const f = function() {};
const g = function() {};
const h = function() {};
禁止对函数参数再赋值
在函数括号内使用一致的换行,如果是多行会要求最后一项带逗号
// normal
// 不使用换行
function foo(bar, baz, quux) {
// ...
}
// bad
// 会报function-paren-newline要求使用一致的换行
function foo(bar, baz, quux) {
// ...
}
// good
// 最后一行添加了逗号
function foo(bar, baz, quux) {
// ...
}
- 箭头函数
- 当必须使用匿名函数时(比如回调函数),可以使用箭头函数
// 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;
});
- 如果回调函数只是简单的单行语句,可以省略 return
// good
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
// good,提高可读性
[1, 2, 3].map(x => (x + 1) * x);
- 如果表达式垮多行,将其包裹在括号中,可以提高代码的可读性
// not good
["get", "post", "put"].map(httpMethod =>
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
);
// good
["get", "post", "put"].map(httpMethod =>
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
);
- 箭头函数体只有一个参数时且不使用大括号,可以省略圆括号。其它任何情况,参数都应被圆括号括起来
// bad
// arrow-parens
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(x => x * x);
// bad
// 因为此时使用了大括号,箭头函数后面跟随了代码块
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
- 禁止在可能与比较操作符相混淆的地方使用箭头函数
// bad
// 此时的箭头符号和比较符号看起来很像
const itemHeight = item =>
item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item =>
item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
- 模块
使用模块 import/export 输入输出代码块
使用 import 时,不要使用通配符,最好明确要导入的代码块
// 另外一个模块文件,比如是index.js
export const firstName = "rede";
export const lastName = "li";
// 引用该模块
// bad
// 按webpack 4+以上的版本会静态分析index.js只导入需要的代码块,所以明确导入的代码块会更有利于减少打包体积
import * as index from "./index";
console.log(index.firstName);
// best
// 原例子上还提到了一个good的写法,不过看了下,感觉这种写法最好,结合编译器,还能减小文件代码
import { firstName } from "./index";
console.log(firstName);
- 不要从 import 中直接导出(export)
// bad
export { firstName as default } from "./index";
// good
// 这样更加明晰
import { firstName } from "./index";
export default firstName;
- 同一个路径的导入只在一个地方导入,禁止重复导入
// bad
import { firstName } from "./index";
import { lastName } from "./index";
export default { firstName, lastName };
// good
import { firstName, lastName } from "./index";
export default { firstName, lastName };
- 不要 export 可变绑定
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
- 如果一个文件只导出一个模块,默认 export 优于命名 export
// bad
const foo = 3;
export { foo };
// good
const foo = 3;
export default { foo };
- 将所有 import 导入放在非导入语句上面
// bad
import { firstName, lastName, backName } from "./index";
backName();
import { name } from "./test";
export default { firstName, lastName, name };
// good
import { firstName, lastName, backName } from "./index";
import { name } from "./test";
backName();
export default { firstName, lastName, name };
- 多行导入应该像多行数组那样进行缩进
// bad
import { firstName, lastName, year } from "./main";
export { firstName, lastName, year };
// good
import { firstName, lastName, year } from "./main";
export { firstName, lastName, year };
- 禁止在模块的 import 中使用 Webpack 加载语法
// bad
import fooSass from "css!sass!foo.scss";
// good
import fooSass from "foo.scss";
- 属性
- 使用点语法来访问对象属性
// bad
const luke = {
jedi: true,
age: 28
};
const isJedi = luke["jedi"];
export default { isJedi };
// good
const luke = {
jedi: true,
age: 28
};
const isJedi = luke.jedi;
export default { isJedi };
- 当通过变量访问属性时要使用中括号
const luke = {
jedi: true,
age: 28
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp("jedi");
export default { isJedi };
图片/iconfont 规范
- 所有页面元素类图片均放入 img 文件夹,测试用图片放于 demo 或其他具有明确描述的文件夹中,以备上线后清理冗余静态资源。
- 图片格式 gif/png/jpg。提倡使用 webp 文件格式,有条件使用软件进行图片压缩。
- 命名全部用小写英文字母||数字||-的组合;尽量用易懂的词汇,便于团队其他成员理解。
- 在保证视觉效果的情况下选择最小的图片格式与图片质量,以减少加载时间。单个图片文件大小控制在 80kb 以下。
- 运用 css sprite 技术集中小的背景图或图标,减小页面 http 请求,但注意,请务必在对应的精灵图 psd 源图中划参考线,并保存至 img 目录下。
- base64 可以替代一些小图片,同样也可以起到减少请求的作用。体积过大的不建议使用。
- 重要图片必须加上 alt 属性。给重要的元素和截断的元素加上 title。