前端编码规范

旨在增强团队协作、提高代码质量,保持代码最大程度的一致性、降低维护代码的成本。

# 命名规范

约定前端项目中合法的命名格式有以下4种(后续提及命名风格时直接以英文展示):

  1. camelCase: 小驼峰式命名
  2. PascalCase: 大驼峰式命名
  3. kebab-case: 短横线连接式命名
  4. CONSTANT_CASE: 全大写下划线式命名

# 通用命名

  1. 强制命名禁止使用拼音与英文混合的方式,更不允许直接使用中文的方式。

😐说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免。

😊正例:discount / seckill / rmb (国际通用的名称可视为英文)

😨反例:dazhe [打折] / miaosha [秒杀]

  1. 强制使用完整的单词组合来表达,名称具有语义性,可以体现功能。

😐说明:代码要避免不规范的缩写和随意命名,严重降低了代码的可阅读性。

😊正例:function dateFormat() {} / let orderCount = 1 / class="user-avatar"

😨反例:function a() {} / let b = 1 / class="c"

  1. 强制禁止使用特殊字符命名。

😨反例:user& / user< / user"

  1. 强制代码中变量、函数和参数等不要使用保留关键字。
  2. 推荐不要使用和广告挂钩的单词做样式和页面路径,因为有些浏览器插件(Chrome的广告拦截插件等)可能会过滤这些命名,导致页面访问异常。

😨反例:ad / banner / gg / guanggao

  1. 建议项目中使用增删改查、详情等功能时,统一使用以下单词
    • add: 新增/添加/创建,如views/product/add, views/shop/add-product
    • edit: 编辑/修改,如views/product/edit, views/shop/edit-product
    • delete: 删除,如views/product/delete, views/shop/delete-product
    • detail: 详情/明细,如views/product/detail, views/shop/product-detail
    • list: 查询列表/记录,如views/product/list, views/shop/product-list

# 目录文件命名

  1. 强制项目命名采用kebab-case命名,最好能体现项目特色。

😐说明:利用项目中的主要功能特点,结合项目终端类型命名,可以更好归类项目。

😊正例:salesman-admin(业务员后台管理系统) / salesman-mp(业务员小程序)

😨反例:saleAdmin / SaleMp / sale_admin / my-project

  1. 强制目录/文件夹命名采用kebab-case命名,有复数结构时,要采用复数命名法,普遍认同的目录名称不用复数。

😐说明:这些目录无需遵循复数命名风格:src / store / router / static / config / api / wxs

😊正例:styles / utils / assets / common-setting

😨反例:style / util / img

  1. 强制文件命名采用kebab-case命名,体现文件代表的功能和特点等。

😐说明:项目中所有文件,包括js/ts、css/stylus/less/scss、html/vue和图片等。

😊正例:time-mixin.js / product-detail.scss / none-data.vue

😨反例:a.js / birthdayPackage.vue / BaseAvatar.png

  1. 强制除了需要动态适配的文件,其他文件不得简单添加数字、字母等非语义性字符区分。

😐说明:可能有些图片是区分主题的,例如theme为1,2,3...,这样图片路径可以动态拼接src=static/logo-${theme}.png,为此不做限制。

😊正例:auth-btn.vue / middle-btn.vue / user.js

😨反例:btn1.vue / btn2.vue / btn-copy.vue / api1.js / api-t.js

# HTML命名

  1. 强制标签名、类名、标签属性和属性值统一采用小写。

😐说明:虽然HTML语言不区分大小写,但是小写看起来更纯净,统一采用小写。

  1. 强制id属性值保持唯一,即在整个HTML范围内只能出现一次。

😊正例:

<div id="topHeader"></div>
<div id="bottomHeader"></div>
1
2

😨反例:

<div id="topHeader"></div>
<div id="topHeader"></div>
1
2
  1. 推荐标签的自定义属性以data- 开头。

😊正例:

<div class="home-page" data-theme="dark"></div>
1

# JS命名

  1. 强制JS中的类和构造函数采用PascalCase命名。

😊正例:function Request() {} / class Request {}

😨反例:function request() {} / class request {}

  1. 强制JS中的变量、函数、方法名、参数名采用camelCase命名。

😐说明:因为接口传参的字段和返回的字段不一定遵从camelCase命名,与接口对接的字段可以原样保存,除此之外一律采用camelCase命名。

😊正例:btnLoading / function getData(pageSize) {}

😨反例:BtnLoading / fucntion getdata(PageSize) {}

  1. 强制JS中的常量采用CONSTANT_CASE命名。

😐说明:常量名力求语义表达清楚、完整,不用担心名称过长,使用const声明。

😊正例:COUNTDOWN_TIME / MAX_STOCK_COUNT

😨反例:count_time / MAX_COUNT

  1. 推荐项目中的请求采用camelCase命名,并且以 api 为前缀。

😐说明:API请求稍微特殊一点,如果采用camelCase命名,可能会跟页面中的变量、方法混淆,通过特定格式apiXxYy更容易区分。

😊正例:apiGetUserInfo

😨反例:getUserInfo / MyInfo / my_detailInfo

# CSS命名

  1. 强制CSS中类名采用kebab-case命名。

😊正例:class="user-avtar"

😨反例:class="my_header" / class="myHeader" / class="MyHeader"

  1. 强制CSS中id采用camelCase命名。

😊正例:id = “userAvatar"

  1. 强制scss中的变量、mixin采用kebab-case命名。

😊正例:$state-prefix / @mixin scroll-bar

  1. 强制scss中的函数采用camelCase命名。

😊正例:@function containWhenFlag() {}

# Vue命名

  1. 强制组件name为多个单词,采用PascalCase命名。

😐说明:HTML元素都是单个单词,组件name使用多个单词的组合,可以避免跟现有的以及未来的HTML元素冲突。

  1. 强制在使用组件的地方统一采用kebab-case命名。

😐说明:由于HTML大小写不敏感,而且在单文件组件、字符串模板和DOM模板,需要区分组件名,为此统一约定采用kebab-case命名。

  1. 强制prop声明采用camelCase命名,在template中使用时属性采用kebab-case命名。

😐说明:单纯遵循每个语言的约定,JavaScript中更自然的是camelCase,而在HTML中是kebab-case

😊正例:(涉及1-3点)

// components/md-table.vue
export default {
  name: 'MdTable', // 1.组件名PascalCase
  props: {
    tableLoading: { // 3.prop声明camelCase
      type: Boolean,
      default: false
    }
  }
}
// test.vue 
// 2.组件使用kebab-case 3.属性kebab-case
<md-table :table-loading="tableLoading"></md-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 强制Prop声明不要使用保留关键字和data / methods等Vue选项名称。

😐说明:Prop不能使用关键字,与Vue选项相同会有覆盖的风险。

😊正例:

value: {
  type: Object,
  default () {
    return {}
  }
}
1
2
3
4
5
6

😨反例:

data: {
  type: Object,
  default() {
    return {}
  }
}
1
2
3
4
5
6
  1. 强制vue中的引用(ref)采用camelCase命名。

😐说明:ref被用来给元素或子组件注册引用信息,绑定在父组件的refs对象中,根据引用使用在DOM元素或子组件,引用指向DOM元素或子组件实例。当ref与v-for结合时,引用是一个数组。

😊正例:

<!-- this.$refs.testTag 包含三个数组实例-->
<el-tag v-for="i in 3" ref="testTag" :key="i" type="success">
   test tag
</el-tag>
1
2
3
4
  1. 强制路由path采用kebab-case命名。

😐说明:尽量和vue文件的目录结构保持一致,path和文件名都是kebab-case,这样方便查找文件。

😊正例:

// router.js
path: '/shop/product-list'
1
2
  1. 强制路由name采用PascalCase命名。

😐说明:一般情况下与所对应的.vue文件中的name字段保持一致。

😊正例:



 





 



// product-list.vue
export default {
  name: 'ProductList',
}

// router.js
{
  path: '/shop/product-list',
  name: 'ProductList',
  component: () => import('@/views/shop/product-list.vue')
}
1
2
3
4
5
6
7
8
9
10
11
  1. 强制 uni-app项目页面路径采用kebab-case命名,对应`page.json`的path配置。

😊正例:

// page.json
{
  "path": "pages/member/card-list"
}
1
2
3
4

# HTML规范

HTML规范适用于普通html文件、Vue里面的template模板和小程序的WXML文件。

# 格式规范

  1. 强制标签必须嵌套正确,统一使用2个空格缩进。

😊正例:

<div>
  <p></p>
</div>
1
2
3
  1. 强制标签必须合法且闭合,空元素和没有内容的组件自闭合。

😐说明:常见的HTML空元素:img / input / link / meta

😊正例:

<img src="" alt="" />
<my-component />
1
2
  1. 强制元素属性值使用双引号(")包括,boolean类型的属性可不加属性值。

😐说明:boolean属性不加属性值,默认生效,如需动态切换还是需要书写完整。

😊正例:

<el-button type="primary" plain>提交</el-button>
<el-button type="danger" :loading="btnLoading">删除</el-button>
1
2
  1. 推荐使用空行分隔大型或逻辑代码块。

😐说明:为了提高可读性,可以根据页面中的功能模块,用一个空行分隔。

  1. 推荐单行代码字符不要超过80,超过三个属性换行。

😐说明:每个属性单独一行,这样更清晰、易阅读。

😊正例:

<el-date-picker
   v-model="form.time"
   type="date"
   placeholder="选择日期"
   class="w-300"
></el-date-picker>
1
2
3
4
5
6

# 语言规范

  1. 强制html文件必须加上DOCTYPE声明,并统一使用HTML5的文档类型声明

<!DOCTYPE html>。同时声明页面语言<html lang="zh-CN">,字符集<meta charset="UTF-8">

😊正例:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
  </head>
</html>
1
2
3
4
5
6
  1. 推荐标签尽量保证语义性,减少标签数量,DOM节点过多会影响页面渲染。

😊正例:

<header></header>
<main>
   <nav></nav>
   <section></section>
</main>
<footer></footer>
1
2
3
4
5
6

😨反例:

<div class="header-container">
  <div class="header"></div>
</div>
1
2
3
  1. 推荐使用 class 选择器实现样式,避免 id 和标签等选择器强耦合。

# 注释规范

HTML注释语法为<!-- 注释内容 -->。在<!--之后增加一个空格,在-->之前增加一个空格。

  1. 推荐页面一个完整的功能、模块增加相关说明。

😊正例:

<!-- 优惠券 -->
<div class="coupon-container"></div>
1
2
  1. 推荐功能、模块代码较多的,可以标注起始段和结束段。

😊正例:

<!-- 秒杀 开始 -->
<div class="seckill-container">
  ...
</div>
<!-- 秒杀 结束 -->
1
2
3
4
5

# JS规范

# 格式规范

  1. 强制使用2个空格缩进。
  2. 强制单行代码字符不要超过80,超出需拆分多行,提高代码可读性和可维护性。

😊正例:

const foo = { bar: 'This is a bar.', 'baz': { 'qux': 'This is a qux' }, 'difficult': 'to read' }
1

😨反例:

const foo = {
  bar: 'This is a bar.',
  baz: { qux: 'This is a qux' },
  easier: 'to read'
}
1
2
3
4
5
  1. 强制代码后面无需加分号(;)。

😐说明:当JavaScript遇见一个没有分号的换行符时,JavaScript引擎会使用自动分号插入规则(automatic semicolon insertion)视换行符为结束。特殊语法需要添加分号,防止报错,常见的场景有:三目表达式函数前面的语句,解构赋值括号前。

😊正例:


 


 

this.checkTimes()
this.btnLoading = true;
(this.isAdd ? API_APPOINTMENT_ADD : API_APPOINTMENT_EDIT)()

;({ result, msg } = rules[check](val, errMsg))
1
2
3
4
5
  1. 强制统一使用单引号(')定义字符串,而不是双引号(")。

😐说明:ES6中模板字符串的反引号(`)也是支持的,但推荐在包含插值或换行时使用。

😊正例:

let result = ''
let result = `${msg}`
1
2

😨反例:

let result = ""
1
  1. 强制大括号风格。如果大括号内容为空,可以{}表示,无需换行;如果非空则左大括号前不换行,左大括号后换行;右大括号前换行,右大括号后还有else等代码块不换行,表示终止的右大括号必须换行。

😊正例:

if (firstName === 'hello') {
  console.log(firstName)
} else {
  console.log(lastName)
}
// 以及
if (firstName === 'hello') {
  return
}
console.log(lastName)
1
2
3
4
5
6
7
8
9
10

😨反例:

if (firstName === 'hello') {
  console.log(firstName)
} 
else {
  console.log(lastName)
}
// 以及
if (firstName === 'hello') 
{
  console.log(firstName)
} 
else 
{
  console.log(lastName)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 强制统一空格维持一致性。

😐说明:以下几种情况需要添加空格

(1)算数运算符(+ - * / %)、赋值运算符(= += -= *= /= %=)、比较运算符(== === != !== > < >= <= ?:)、逻辑运算符(&& || !)的前后加一个空格

(2)代码块左大括号({)前加一个空格

(3)关键词(else while catch finally)前加一个空格

(4)对象的属性值前加一个空格

(5)函数声明或函数表达式的左大括号({)前加一个空格

(6)关键词(if else for while do switch case try catch finally return typeof)后加一个空格

(7)for循环分号(;)后面加一个空格

(8)逗号前避免使用空格,逗号后面加一个空格

😊正例:(涉及1-8点)

// 1.运算符前后加空格
const person = {
  firstName: 'hello',// 4.对象属性前加空格
  lastName: 'world'
}
// 5.函数声明 `{` 前加空格
// 8.函数参数多个,逗号后加空格
function log({ firstName, lastName }) {
  // 2.代码块 `{` 前加空格
  if (firstName === 'hello') {
    console.log(firstName)
    // 3.关键词前加空格
    // 6.关键词后加空格
  } else {
    console.log(lastName)
  }
}
log(person)
// 7.for循环分号后加空格
for (let i = 0; i < 5; i++) {
  console.log(i)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  1. 强制不使用拖尾逗号。

😐说明:拖尾逗号可以使git差异更清洁,但是没有设置好babel编译选项的话,可能个别机型/浏览器会报错。统一约定在最后一个元素或属性不使用拖尾逗号。

😊正例:

const person = {
  firstName: 'hello',
  lastName: 'world'
}

const languages = ['React', 'Vue']
1
2
3
4
5
6

😨反例:

const person = {
  firstName: 'hello',
  lastName: 'world',
}
const languages = ['React', 'Vue',]
1
2
3
4
5
  1. 推荐不同逻辑、不同定义、不同业务代码之间插入一个空行,文件末尾插入一个空行。

😐说明:通过空行在视觉上对代码进行分组,也能更方便地知道,后续新加的逻辑的所在位置。任何情形,没有必要插入多个空行进行分隔。

# 语言规范

  1. 强制变量必须先声明后使用,使用let/const替代var,而且const性能更佳。

😐说明:优先使用const声明,如果需要重新赋值的,使用let声明。const声明可以避免因重新赋值导致的错误或产生难以理解的代码。

😊正例:

let result = '' 
const COUNTDOWN_TIME = 60
const obj = {}
1
2
3

😨反例:

var result = '' 
const countdownTime = 60
1
2
  1. 强制将所有的import语句放在所有非导入语句的最上边。

😐说明:由于所有import被提前,保持它们在顶部是为了防止意外发生。

  1. 强制禁止随意使用console,大量使用console会有性能问题。

😐说明:杜绝输出一些简单的日志仅用于判断某个逻辑是否执行,这些应该调试完删除。可以使用的地方:输出请求信息以便定位接口,输出捕获的异常信息。

😊正例:

api().then(res => {}).catch(err => {
  console.log(err)
})
1
2
3

😨反例:

private handleSearch() {
    this.getFindAppraiseList(1, this.page.size)
    console.log(this.filter, '111111', this.createTime)
}
1
2
3
4
  1. 强制代码中禁止出现debugger等断点调试语句,这些应该调试完删除。

😐说明:可以利用浏览器或调试工具的sources面板进行调试,且功能更强大。

  1. 强制禁止使用arguments定义参数名称,且不要对参数赋值。

😐说明:定义参数为arguments,会优先于函数给定范围的arguments对象。重新赋值参数会导致意外的行为,可以使用参数默认值语法。

😊正例:

function log(name = 'hello', args) {}
1

😨反例:

function log(name, arguments) {
name = 'prevent'
}
1
2
3
  1. 强制使用对象字面量语法创建对象、数组字面量创建数组。

😊正例:

const obj = {}
const arr = []
1
2

😨反例:

const obj = new Object()
const arr = new Array()
1
2
  1. 强制不要直接使用undefined判断变量,通过typeof'undefined'比较。

😊正例:

if (typeof name === 'undefined')
1

😨反例:

name === undefined
1
  1. 强制定时器任务(setTimeout / setInterval等)及时销毁。

😐说明:非业务需要前提下,在页面隐藏、页面路由变化时销毁或暂停定时器任务,特别是setInterval,不然相关逻辑依旧在后台执行。

  1. 推荐函数里面的参数不要使用arguments,使用剩余运算符(...)。

😐说明:arguments只是一个类数组,而剩余运算符是一个真正的数组。

😊正例:

function test(...args) {
  return args.map(item => item * 2)
}
1
2
3

😨反例:

function test() {
  const args = Array.prototype.slice.call(arguments)
  return args.map(item => item * 2)
}
1
2
3
4
  1. 推荐使用对象属性的简洁表达式。

😐说明:在对象直接写入变量和函数,作为属性和方法。

😊正例:

const name = 'hello'
const language = {
  name,
  sayName() {
    console.log(this.name)
  }
}
1
2
3
4
5
6
7

😨反例:

const name = 'hello'
const language = {
  name: name,
  sayName: function() {
    console.log(this.name)
  }
}
1
2
3
4
5
6
7
  1. 推荐浅拷贝对象、数组时,或将一个类数组转换成一个数组时,使用扩展运算符(...)。

😊正例:

const NEW_OPTIONS = { ...DEFAULT_OPTIONS, ...options }

const copyArr = [...originArr]

const elements = document.querySelectorAll('.item')
const nodes = [...elements]
1
2
3
4
5
6

😨反例:

const NEW_OPTIONS = Object.assign({}, DEFAULT_OPTIONS, options)
// 禁止直接操作源对象
const NEW_OPTIONS = Object.assign(DEFAULT_OPTIONS, options)
1
2
3
  1. 推荐在访问和使用对象的多个属性时,优先使用对象的解构。

😐说明:解构可以避免为这些属性创建临时引用。

😊正例:

function log(user) {
  const { firstName, lastName } = user
  return `${firstName} ${lastName}`
}
// 更好
function log({firstName, lastName}) {
  return `${firstName} ${lastName}`
}
1
2
3
4
5
6
7
8

😨反例:

function log(user) {
  const firstName = user.firstName
  const lastName = user.lastName
  return `${firstName} ${lastName}`
}
1
2
3
4
5
  1. 推荐对于返回多个值的函数使用对象,而不是数组。

😐说明:使用对象就可以随时添加新的属性或者改变属性的顺序,调用方不用修改。

😊正例:

function processInput(input) {
  // 处理代码...
  return { left, right, top, bottom }
}

// 调用者只选择他们需要的数据。
const { left, top } = processInput(input)
1
2
3
4
5
6
7

😨反例:

function processInput(input) {
  // 处理代码...
  return [left, right, top, bottom]
}

// 调用者需要考虑返回数据的顺序。
const [left, _, top] = processInput(input)
1
2
3
4
5
6
7
  1. 推荐使用JS更高优先级的函数代替forfor-infor-of循环。

😐说明:使用map()、every()、some()、filter()、find()、findIndex()、reduce()...等遍历数组,若有需要,使用Object.keys()、Object.values()、Object.entries()迭代对象生成数组。

😊正例:

const numbers = [1, 2, 3]

let sum = 0
numbers.forEach(num => sum += num)
// 更好
const sum = numbers.reduce((total, num) => total + num, 0)
1
2
3
4
5
6

😨反例:

let sum = 0
for (let num of numbers) {
  sum += num
}
1
2
3
4
  1. 推荐使用严格比较运算符即===!==,而不是==!=

😊正例:

typeof check === 'function'
1

😨反例:

typeof check == 'function'
1
  1. 推荐函数设计应当遵循低耦合的基本原则,一个函数完成单一的逻辑,复杂的逻辑进一步抽取,特定算法等不可(适合)分割的逻辑除外。
  2. 推荐善于利用三元表达式、短路运算符等优化if条件判断。

😊正例:

res = status === 200 ? '成功' : '失败'
isValid && func()
1
2

😨反例:

if (status === 200) {
  res = '成功'
} else {
  res = '失败'
}
if (isValid) {
  func()
}
1
2
3
4
5
6
7
8
  1. 推荐TypeScript中,声明函数、变量的类型,全局变量、函数、接口需先定义,书写内部空间和模块等声明文件(*.d.ts)。
  2. 建议用好TypeScript的类型检查,尽量从根本原因解决一些警告和错误,而不是使用// @ts-ignore等跳过检验。

# 注释规范

JavaScript注释语法分为单行注释(// 注释内容)和多行注释(/* 注释内容 */)。

😐说明:单行注释中,注释文字与//之间保留一个空格;多行注释建议在至少三行注释时使用,第一行为/**,最后为*/,中间行以*开始,注释文字与*之间保留一个空格。

😊正例:

// 单行注释

/**
 * 多行注释
 */
1
2
3
4
5
  1. 推荐核心方法、业务逻辑强相关代码,添加注释,简述功能目的、入参出参(若有)等,采用多行注释。

😐说明:代码本身是清晰的、可读性强的,可以无需注释。

😊正例:

/**
 * 防抖函数
 * @param delay 延迟 单位毫秒,默认300
 * @param options leading: true先调用后等待 trailing:true先等待后调用
 *                都为true则可以多次调用
 * @returns {Function}
 */
function Debounce(
  delay = 300,
  options = { leading: true, trailing: false }
) {}
1
2
3
4
5
6
7
8
9
10
11
  1. 推荐项目中封装的逻辑,增加使用说明,采用多行注释。

😊正例:

/**
 * 规则封装
* e.g.:  mobile: [validate({ check: 'mobile', nullMsg: '请输入手机号' })]
 * @param required 是否必填,默认true
 * @param trigger 触发操作
 * @param nullMsg required=true时提示信息
 */
function validate() {}
1
2
3
4
5
6
7
8
  1. 推荐业务逻辑使用的变量,增加说明,包括含义,可能出现的值,采用单行注释。

😊正例:

let popupType = 1 // 弹窗类型 1.xx 2.yy
1

# CSS规范

# 格式规范

  1. 强制使用2个空格缩进。
  2. 强制每个属性声明以分号(;)结尾。
  3. 强制统一使用展开格式,每个选择器及属性单独一行。
  4. 强制使用多个选择器时,每个选择器单独一行,逗号在前一个选择器末尾。
  5. 强制每个规则集包含在大括号里({}),左大括号不换行,右大括号换行。
  6. 强制统一空格维持一致性。

😐说明:以下几种情况需要添加空格

(1)属性值前

(2)选择器(> + ~)前后

(3)左大括号({)前

(4)!important

  1. 强制样式选择器、属性名、属性值关键词全部小写。

😊正例:(涉及1-7点)

.el-input,
.el-radio {
  color: #333;
}
1
2
3
4

😨反例:

.el-input,.el-radio{
  color:#333;
}
1
2
3
  1. 强制不允许存在空的规则,重复的规则。
  2. 推荐去掉小数前面的0,去掉数字中不必要的小数点和0。
  3. 推荐去掉属性值0后面的单位。
  4. 推荐颜色使用16进制小写字母,尽量简写。

😊正例:(涉及8-11点)

.test {
  width: 16px;
  height: 0;

  color: #f40;
  background-color: rgba(255, 255, 255, .1);
}
1
2
3
4
5
6
7

😨反例:

.test {
  color: #FF4400;
  background-color: rgba(255, 255, 255, 0.1);
  width: 16.0px;
  height: 0px;
}
.header {}
1
2
3
4
5
6
7
  1. 推荐SCSS中使用@include时,放在普通属性后面。如果有嵌套选择器,空一行再定义嵌套选择器。

😊正例:

.el-input {
  border: none;
  @include round(5px);

  input {
    font-size: 14px;
  }
}
1
2
3
4
5
6
7
8
  1. 建议如果了解属性值的顺序,而且需要设置几个属性值,可以采用简写。否则的话,分开书写更清晰。

😊正例:

.element {
  transition-delay: 1s;
  transition-timing-function: linear;
  transition-duration: .5s;
  transition-property: opacity;
}
// 相当于
.element {
  transition: opacity .5s linear 1s;
}
1
2
3
4
5
6
7
8
9
10

# 语言规范

  1. 强制避免样式嵌套层级过深,最好限制在3级,CSS选择器的匹配是从右向左进行的。虽然页面层级可能嵌套很深,但是css不需要完全按照页面层级书写。
  2. 强制公共样式写在common.scss,页面优先使用通用样式,特殊需要覆盖再重新书写。

😊正例:

// common.scss
.tab-container {
  height: 60px;
  background-color: #fff;
}

// test.vue <style>
.tab-container {
  height: 54px;
}
1
2
3
4
5
6
7
8
9
10
  1. 强制尽量用class选择器,少用甚至不用id、标签和属性等选择器。

😐说明:id选择器样式无法复用,同时防止开发过程中id名或标签名等变化,引起页面布局错乱。

  1. 推荐无需兼容低配机型和浏览器时,优先考虑使用flex布局,就算重排也会比inline-block和float快。
  2. 推荐定义variable.scss变量文件,然后在页面/组件等需要的地方采用引入的方式,可以方便整体控制应用的风格。

😐说明:uni-app项目中存在一个uni.scss文件,页面/组件无需导入这个文件,在此定义的变量,页面/组件可直接使用变量。

  1. 建议将相关属性顺序放在一组,提高代码可读性。

😐说明:参考CSScomb yandex (opens new window)配置,建议遵循的顺序为

(1)布局定位属性:position、display、float、flex等
(2)盒子模型属性:box-sizing、width、height、margin、padding等
(3)过渡动画属性:transition、animation等
(4)文本属性:text-align、white-space、text-decoration等
(5)外观属性:opacity、color、border、outline、background等
(6)字体属性:font、font-size、line-height等

  1. 建议根选择器限制唯一性,子选择器可以使用不同命名区分。

😐说明:可选择BEM命名风格,格式为[namespace-]block__element--modifier,namespace一般为组件名称。

# Vue规范

Vue单文件分为template、script和style三大部分,分别参考HTML规范、JavaScript规范和CSS规范。Vue规范针对Vue2.x版本,除了遵循上述规范外,摘取Vue官方规范中必要的和强烈推荐的风格(更多的可以参考Vue风格指南),以及开发过程中的一些优化建议。

# 格式规范

  1. 强制单文件应该有统一的顺序。

😐说明:单文件中应该让<template><script><style>标签保持顺序一致,且<style>放在最后,因为<style>是可选的。

😊正例:

<template>
 
</template>

<script>

</script>

<style>

</style>
1
2
3
4
5
6
7
8
9
10
11
  1. 推荐选项应该有统一的顺序。

😊正例:

 name/parent
 components/directives/filters
 mixins
 inheritAttrs/model/props/propsData
 data/computed
 watch
 生命周期
 methods
1
2
3
4
5
6
7
8
  1. 推荐生命周期顺序,按照它们被调用的顺序依次定义。

😊正例:

beforeCreate --> created --> beforeMount --> mounted
beforeUpdate --> updated 
activated --> deactivated --> beforeDestory --> destroyed
1
2
3
  1. 推荐元素(含组件)的属性应该有统一的顺序。

😊正例:

is
v-for
v-if/v-else-if/v-else/v-show/v-cloak
v-pre/v-once
id
ref/key
v-model
其他attribute:
  class
  name
  data-*
  src/for/type/href/value
  max-length/min-length/max/min/paterrn
  placeholader/title/alt
  aria-*/role
  required/readonly/disabled/checked/selectd
v-on 
v-html/v-text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. 强制样式顺序

😐说明:style按导入(@import)、变量($variable)、选择器的顺序书写。

😊正例:

@import "~@/styles/variable.scss";
$page: home-page;
.#{$page} {
  padding: 20px;
}
1
2
3
4
5
  1. 推荐绑定语法(双大括号{{}})内容左右两边添加一个空格。

😊正例:

<el-tag type="primary">{{ count }}</el-tag>
1

😨反例:

<el-tag type="primary">{{count}}</el-tag>
1
  1. 推荐指令都使用缩写形式( :表示v-bind:@表示v-on:#表示v-slot:)。

😊正例:

 <md-table
  :table-data="tableData"
  :columns="headers"
  @getData="getTableData"
 >
    <template #operate>
      <el-button type="primary" size="mini">编辑</el-button>
    </template>
</md-table>
1
2
3
4
5
6
7
8
9

# 语言规范

  1. 强制组件的data必须是返回对象的函数。

😐说明:除了使用new Vue的任何地方,都必须是返回函数的对象。因为直接使用一个对象,会使得这个组件的所有实例共享数据。

😊正例:

  data() {
    return {
      headers: []
    }
  }
1
2
3
4
5

😨反例:

  data: {
      headers: []
  }
1
2
3
  1. 强制prop定义尽量详细,必须指定类型,必须指定required或者default。如果有业务需要,必须加上validator验证。

😊正例:

props: {
  tableLoading: {
    type: Boolean,
    default: false
  }
}
1
2
3
4
5
6

😨反例:

props: ['tableLoading']
1
  1. 强制v-for设置key属性,并且属性值是唯一的,有利于维护内部组件及其子树的状态。

😊正例:

<el-option v-for="item in 3" :key="item" :value="item">
    {{ item }}
</el-option>
1
2
3

😨反例:

<el-option v-for="item in 3"  :value="item">
    {{ item }}
</el-option>
1
2
3
  1. 强制v-forv-if不能用在同一个元素上。

😐说明:Vue处理指令时,v-for比v-if优先级高,渲染时会遍历整个列表,然后进行v-if逻辑判断,影响性能。可以解耦渲染逻辑,提前v-if判断,或者先计算出符合v-if条件的数据再v-for。

😊正例:

<template v-if="isActiveUser">
   <div v-for="item in userList" :key="item.userId"></div>
</template>
<!-- 或者 利用computed计算出activeUserList -->
<div v-for="item in activeUserList" :key="item.userId"></div>
1
2
3
4
5

😨反例:

<div
   v-for="item in userList"
   v-if="item.isActive"
   :key="item.userId">
</div>
1
2
3
4
5
  1. 强制组件模板不要包含复杂表达式,应该重构为计算属性或方法。

😊正例:

// 模板内使用 {{ normalizedFullName }}
computed: {
  normalizedFullName() {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}
1
2
3
4
5
6
7
8

😨反例:

{{
  fullName.split(' ').map(function (word) {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}
1
2
3
4
5
  1. 强制style添加scoped属性,限制作用域,防止污染全局样式。
  2. 推荐data声明中对象层次不要超过4层。

😐说明:对象嵌套太深一方面对性能有影响,因为Vue2.x版本是利用Object.defineProperty递归实现数据响应式变化的;另一方面获取某个属性值时不方便。

😨反例:

data() {
    return {
      form: {
        person: {
          name: '',
          job: {
            title: '',
            desc: ''
          }
        }
      }
    }
  }
// this.form.person.job.title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 推荐利用Object.freeze()提升展示类数据渲染性能。

😐说明:Object.freeze()可以冻结一个对象,冻结之后不能增加新属性,不能删除已有属性,不能修改属性值,不能更改对象配置。Vue赋值变量为一个冻结对象后,不会生成getter和setter劫持方法。对于纯展示类、无需变化的数据,可以使用,在数据量越大时性能提升越显著。Object.freeze()冻结的是值,仍然可以将变量的整个引用替换掉。

😊正例:

data() {
  return {
    list: []
  }
}
async created() {
  const list = await API_GET_LIST()
  this.list = Object.freeze(list)
},
methods: {
  async updateList() {
    const list = await API_POST_LIST()
    this.list = Object.freeze(list)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 推荐应该把复杂的计算属性拆分多个且简单的计算属性。

😐说明:不要在一个计算属性内包含太多逻辑,尽量拆分出几个功能相对单一的计算属性,最终组合成所需的计算属性。这样不仅易于测试和阅读,而且就算后期调整也更好重构。

😊正例:

isValid() {
     return this.addressList && this.addressList.length > 0
  },
  chooseAddress() {
     let chosedAddress = null
     if (this.chooseAddressId) {
          chosedAddress = this.addressList.find(
           item => item.Id === this.chooseAddressId
        )
     }
     return chosedAddress || null
  },
  curAddress() {
     if (this.isValid) {
        const defaultAddress = this.addressList.find(item => item.IsDefault)
        return this.chooseAddress || defaultAddress || this.addressList[0]
     }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

😨反例:

curAddress() {
  if (this.addressList && this.addressList.length > 0) {
    const defaultAddress = this.addressList.find(item => item.IsDefault)
    let chooseAddress = null
    if (this.chooseAddressId) {
      const chosedAddress = this.addressList.find(
        item => item.Id == this.chooseAddressId
      )
      chosedAddress && (chooseAddress = chosedAddress)
    }
    return chooseAddress || defaultAddress || this.addressList[0]
  }
  return null
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# uni-app规范

以遵从Vue规范为前提,扩展uni-app特有规范。

# 格式规范

  1. 推荐生命周期按照它们被调用的顺序依次定义。

(1)页面生命周期

😊正例:

onLoad
onShow
onReady
onHide
onUnload
onPullDownRefresh
onReachBottom
onTabItemTap
onShareAppMessage
onPageScroll
1
2
3
4
5
6
7
8
9
10

(2)组件生命周期

😊正例:

beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestory
destroyed
1
2
3
4
5
6
7
8

# 语言规范

  1. 强制优先使用uni-app组件元素,即viewimagepicker等。

😐说明:标签靠近微信小程序规范。不推荐使用HTML标签,实际上div等标签也会被编译器转化。但为了管理方便、策略统一,建议使用view等组件。

  1. 强制接口能力(JS API)优先使用uni前缀。

😐说明:接口能力靠近微信小程序规范,但是uni-app项目中需将wx前缀替换为uni。如果各个小程序平台有新功能而uni-app未兼容的,可以使用各平台的前缀。

  1. 推荐条件编译解决各端差异。

😐说明:uni-app已将常用的组件、JS API封装到框架中,保证了多平台兼容。但每个平台有一些特性,可能存在一些无法跨平台的情况。为此可以使用条件编译,将不同代码编译到不同平台。

(1)条件编译是使用特殊注释语法作标记,以#ifdef(仅在某平台存在)或#ifndef(除了某平台均存在)加PLATFORM(平台名称,使用||分隔多个平台)开头,以#endif结尾。

(2)不同语法有不同注释写法,js/ts/json使用 // 注释、SCSS使用 /* 注释 */、vue模板使用<!-- 注释 -->

(3)PLATFORM取值:APP-PLUS(APP)、H5、MP-WEIXIN(微信小程序)、MP-ALIPAY(支付宝小程序)、MP-BAIDU(百度小程序)、MP-TOUTIAO(字节跳动小程序)、MP-QQ、MP-360、MP(含各端小程序)、QUICKAPP-WEBVIEW(含联盟、华为快应用)、QUICKAPP-WEBVIEW-UNION(快应用联盟)、QUICKAPP-WEBVIEW-HUAWEI(快应用华为)

(4)static目录可以通过新建不同平台的专有名称(PLATFORM取值小写),实现条件编译,这样静态资源只有特定平台才会编译进去。

(5)在项目根目录创建platforms目录,进一步创建子目录(PLATFORM取值小写),存放不同平台的文件,实现整体目录的条件编译。目录下只支持放置页面文件(vue文件),其他资源建议使用static条件编译。

😊正例:(涉及1-4点)

// vue模板条件编译
<!-- #ifdef MP-WEIXIN -->
<official-account></official-account>
<!-- #endif -->
1
2
3
4
// js条件编译
// #ifndef H5
uni.setClipboardData({})
// #endif
// #ifdef MP-WEIXIN MP-ALIPAY
uni.login()
// #endif

// json条件编译
// #ifdef MP
{
  "path": "pages/index/auth",
  "style": {
    "navigationBarTitleText": "授权登录"
  }
}
// #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SCSS条件编译
.element {
  /* #ifndef H5 */
  height: 100%;
  /* #endif */
}
1
2
3
4
5
6
// static目录条件编译
|___static
|   |__mp-weixin
|       |__wx-logo.png
|   |__mp-alipay
|       |__ali-logo.png
|   |__common.png
|___main.js
|___App.vue
1
2
3
4
5
6
7
8
9
  1. 推荐scss文件和<style>标签里面的背景图不使用本地图片。

😐说明:不支持本地图片的平台(mp-weixin、mp-qq、mp-toutiao和app v2),小于40kb会转base64,H5平台小于4kb会转base64,其余平台不会。推荐统一上传至CDN。