前端团队代码规范约定

276 阅读13分钟

1. 前端工程目录说明

├─ api
├─ assets
├─ common
├─ components
│ 	├─business
│ 	└─public
├─ mixin
├─ filter
├─ computed
├─ router
├─ store
├─ utils
├─ views
├─ App.vue
├─ main.js
  • api:接口管理目录
  • assets:静态资源目录,这里的资源会被wabpack构建
  • common:全局配置,第三方库引入定义,常量存放目录
  • mixin:全局vue混入目录
  • filter:全局vue过滤器
  • computed:全局vue计算属性
  • components:目录用来存放模块、组件相关代码
    • business:业务组件目录,跟具体业务逻辑挂钩的组件
    • public:公共组件目录,不跟具体业务逻辑挂钩,可多地方共同使用,组件内不可有具体业务逻辑diam
  • router:路由目录
  • store:vuex目录
  • urils:工具函数目录
  • views:页面组件,具体的页面
  • App.vue:vue入口文件
  • main.js:vue实例初始化文件

注意:组件内不可只放一个vue文件,需要建一个目录来存放vue文件

2. 命名说明

2.0 团队约定命名

  1. 调用接口方法,拼接‘api’前缀,表示该方法是调用接口
  2. 用户主动调用事件拼接‘on’前缀,表示用户主动调用
  3. 数据获取方法拼接‘get’前缀,如:‘getXxxx’:表示获取xxx数据
  4. 带有设置类型的方法拼接‘set’前缀,如:‘setStorage’:表示设置本地缓存
  5. 计算属性,“TODO::”前缀,

2.1 文件命名

  1. 所有文件名统一使用驼峰命名,禁止使用特殊字符

2.2 组件命名

单文件组件的文件名应该始终是单词大写开头 (PascalCase)。

单词大写开头对于代码编辑器的自动补全最为友好,因为这使得我们在 JS(X) 和模板中引用组件的方式尽可能的一致。然而,混用文件命名方式有的时候会导致大小写不敏感的文件系统的问题,这也是横线连接命名同样完全可取的原因。

2.2.1 基础组件名

应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 BaseAppV

这些组件为你的应用奠定了一致的基础样式和行为。它们可能包括:

  • HTML 元素
  • 其它基础组件
  • 第三方 UI 组件库

但是它们绝不会包括全局状态 (比如来自 Vuex store)。

它们的名字通常包含所包裹元素的名字 (比如 BaseButtonBaseTable),除非没有现成的对应功能的元素 (比如 BaseIcon)。如果你为特定的上下文构建类似的组件,那它们几乎总会消费这些组件 (比如 BaseButton 可能会用在 ButtonSubmit 上)。

这样做的几个好处:

  • 当你在编辑器中以字母顺序排序时,你的应用的基础组件会全部列在一起,这样更容易识别。
  • 因为组件名应该始终是多个单词,所以这样做可以避免你在包裹简单组件时随意选择前缀 (比如 MyButtonVueButton)。
反例
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
好例子
components/Base
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

2.2.2 单例组件名

只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。

这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,只是目前在每个页面里只使用一次。

反例
components/
|- Heading.vue
|- MySidebar.vue
好列子
components/
|- TheHeading.vue
|- TheSidebar.vue

2.2.3 紧密耦合的组件名

和父组件紧密耦合的子组件应该以父组件名作为前缀命名。

如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue

2.2.4 组件名中的单词顺序

组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

2.2.5 自闭合组件

单文件组件、字符串模板和 JSX 中没有内容的组件应该是自闭合的——但在 DOM 模板里永远不要这样做。

自闭合组件表示它们不仅没有内容,而且刻意没有内容。其不同之处就好像书上的一页白纸对比贴有“本页有意留白”标签的白纸。而且没有了额外的闭合标签,你的代码也更简洁。

不幸的是,HTML 并不支持自闭合的自定义元素——只有官方的“空”元素。所以上述策略仅适用于进入 DOM 之前 Vue 的模板编译器能够触达的地方,然后再产出符合 DOM 规范的 HTML。

反例
<!-- 在单文件组件、字符串模板和 JSX 中 -->
<MyComponent></MyComponent>
<!-- 在 DOM 模板中 -->
<my-component/>
好例子
<!-- 在单文件组件、字符串模板和 JSX 中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>

2.2.6 模板中的组件名大小写

对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。

PascalCase 相比 kebab-case 有一些优势:

  • 编辑器可以在模板里自动补全组件名,因为 PascalCase 同样适用于 JavaScript。
  • <MyComponent> 视觉上比 <my-component> 更能够和单个单词的 HTML 元素区别开来,因为前者的不同之处有两个大写字母,后者只有一个横线。
  • 如果你在模板中使用任何非 Vue 的自定义元素,比如一个 Web Component,PascalCase 确保了你的 Vue 组件在视觉上仍然是易识别的。

不幸的是,由于 HTML 是大小写不敏感的,在 DOM 模板中必须仍使用 kebab-case。

还请注意,如果你已经是 kebab-case 的重度用户,那么与 HTML 保持一致的命名约定且在多个项目中保持相同的大小写规则就可能比上述优势更为重要了。在这些情况下,在所有的地方都使用 kebab-case 同样是可以接受的。

反例
<!-- 在单文件组件和字符串模板中 -->
<mycomponent/>
<!-- 在单文件组件和字符串模板中 -->
<myComponent/>
<!-- 在 DOM 模板中 -->
<MyComponent></MyComponent>
好例子
<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>
<!-- 在所有地方 -->
<my-component></my-component>

2.2.7 完整单词的组件名

组件名应该倾向于完整单词而不是缩写。

编辑器中的自动补全已经让书写长命名的代价非常之低了,而其带来的明确性却是非常宝贵的。不常用的缩写尤其应该避免。

反例
components/
|- SdSettings.vue
|- UProfOpts.vue
好例子
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

2.3 样式命名

  1. class、id都需小写

  2. 命名使用英文,禁止使用特殊字符

  3. 样式名不能包含adguanggaoadsgg是广告含义的关键词,避免元素被网页拓展、插件屏蔽

  4. 名称间隔使用-符号

  5. 涉及数据、交互等需要联调的部分,禁止通过id选择器定义样式,以免开发过程中id名变化,引起页局错乱

  6. 类名命名需要语义化,参考下面的示例:

     .wrap{}                 //外层容器
     .mod-box{}              //模块容器
     .btn-start{}            //开始
     .btn-download-ios{}     //ios下载
     .btn-download-andriod{} //安卓下载
     .btn-head-nav1{}        //头部导航链接1
     .btn-news-more{}        //更多新闻
     .btn-play{}             //播放视频
     .btn-ico{}              //按钮ico
     .btn-lottery{}          //开始抽奖
     .side-nav{}             //侧栏导航
     .side-nav-btn1{}        //侧栏导航-链接1 
     .btn-more{}             //更多
    

工具:codelf

2.4 ClassName命名

ClassName的命名应该尽量精短、明确,必须以字母开头命名,且全部字母为小写,单词之间统一使用下划线 “_” 连接

祖先模块不能出现下划线,除了是全站公用模块,如 mod_ 系列的命名:

推荐:

<div class="modulename">
	<div class="modulename_info">
		<div class="modulename_son"></div>
		<div class="modulename_son"></div>
		...
	</div>
</div>
	
<!-- 这个是全站公用模块,祖先模块允许直接出现下划线 -->
<div class="mod_info">
	<div class="mod_info_son"></div>
	<div class="mod_info_son"></div>
	...		
</div>

不推荐:

<div class="modulename_info">
	<div class="modulename_info_son"></div>
	<div class="modulename_info_son"></div>
	...		
</div>

在子孙模块数量可预测的情况下,严格继承祖先模块的命名前缀

<div class="modulename">
	<div class="modulename_cover"></div>
	<div class="modulename_info"></div>
</div>

当子孙模块超过4级或以上的时候,可以考虑在祖先模块内具有识辨性的独立缩写作为新的子孙模块

推荐:

<div class="modulename">
	<div class="modulename_cover"></div>
	<div class="modulename_info">
    	<div class="modulename_info_user">
    		<div class="modulename_info_user_img">
    			<img src="" alt="">
    			<!-- 这个时候 miui 为 modulename_info_user_img 首字母缩写-->
    			<div class="miui_tit"></div>
    			<div class="miui_txt"></div>
    			...
    		</div>
    	</div>
    	<div class="modulename_info_list"></div>
	</div>
</div>

不推荐:

<div class="modulename">
	<div class="modulename_cover"></div>
	<div class="modulename_info">
    	<div class="modulename_info_user">
    		<div class="modulename_info_user_img">
    			<img src="" alt="">
    			<div class="modulename_info_user_img_tit"></div>
    			<div class="modulename_info_user_img_txt"></div>
    			...
    		</div>
    	</div>
    	<div class="modulename_info_list"></div>
	</div>
</div>
模块命名

全站公共模块:以 mod_ 开头

<div class="mod_yours"></div>

业务公共模块:以 业务名_mod_ 开头

<div class="paipai_mod_yours"></div>
常用命名推荐

注意:ad、banner、gg、guanggao 等有机会和广告挂勾的字眠不建议直接用来做ClassName,因为有些浏览器插件(Chrome的广告拦截插件等)会直接过滤这些类名,因此

<div class="ad"></div>

这种广告的英文或拼音类名不应该出现

另外,敏感不和谐字眼也不应该出现

2.5 js变量命名

  1. 变量 使用 小驼峰命名法

    var loadingModules = {};
    
  2. 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。

    var HTML_ENTITY = {};
    
  3. 函数 使用 小驼峰命名法

    function stringFormat(source) {
    }
    
  4. 函数参数 使用 小驼峰命名法

    function hear(theBells) {
    }
    
  5. 使用 大驼峰命名法

    function TextNode(options) {
    }
    
  6. 方法 / 属性 使用 Camel命名法

    function TextNode(value, engine) {
        this.value = value;
        this.engine = engine;
    }
    
    TextNode.prototype.clone = function () {
        return this;
    };
    
  7. 枚举变量 使用 大驼峰命名法枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。

    var TargetState = {
        READING: 1,
        READED: 2,
        APPLIED: 3,
        READY: 4
    };
    
  8. 命名空间 使用 小驼峰命名法

    equipments.heavyWeapons = {};
    
  9. 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。

    function XMLParser() {
    }
    
    function insertHTML(element, html) {
    }
    
    var httpRequest = new HTTPRequest();
    
  10. 类名 使用 名词

    function Engine(options) {
    }
    
  11. 函数名 使用 动宾短语

    function getStyle(element) {
    }
    
  12. boolean 类型的变量使用 ishas 开头。

    var isReady = false;
    var hasMoreCommands = false;
    

2.6 图片命名

  1. 图片名称必须小写,禁止使用特殊字符、中文

  2. 使用英文或拼音缩写,禁止特殊字符

  3. 名称间隔使用-符号

  4. 命名需要能体现图片的大概用途

    常用示例:

     bg.jpg          //背景图片
     mod-bg.jpg      //模块背景
     sprites.png     //精灵图
     btn-start.png   //开始按钮
     ico-play.png    //修饰性图片
    
  5. 禁止文件名和实际图片内容不符。反面例子:图片名为'weixin-qrcode',图片内容却是头像。

3. 语言规范

JavaScript 是一种客户端脚本语言,这里列出了编写 JavaScript 时需要遵守的规则。

3.1 类型

原始类型: 存取原始类型直接作用于值本身

  • 布尔类型
  • Null 类型
  • Undefined 类型
  • 数字类型
  • BigInt 类型
  • 字符串类型
  • 符号类型 Symbol
const foo = 1
let bar = foo

bar = 9

console.log(foo, bar) // 1, 9

复杂类型: 访问复杂类型作用于值的引用

  • object
  • array
  • function
const foo = [1, 2, 3]
const bar = foo

bar[0] = 9

console.log(foo[0], bar[0]) // 9, 9

3.2 引用

  • 请记得 constlet 都是块级作用域,var 是函数级作用域
// const and let only exist in the blocks they are defined in.
{
  let a = 1
  const b = 1
}
console.log(a) // ReferenceError
console.log(b) // ReferenceError

原因:这样做可以确保你无法重新分配引用,以避免出现错误和难以理解的代码

// bad
var a = 1
var b = 2

// good
const a = 1
const b = 2
  • 如果引用是可变动的,使用 let 代替 var,eslint: no-var

原因:let 是块级作用域的,而不像 var 属于函数级作用域

// bad
var count = 1
if (count < 10) {
  count += 1
}

// good
let count = 1
if (count < 10) {
  count += 1
}

3.3 对象

  • 请使用字面量值创建对象,eslint: no-new-object

    // bad
    const a = new Object{}
    
    // good
    const a = {}
    
  • 别使用保留字作为对象的键值

    // bad
    const a = {
      default: {},  // default 是保留字
      common: {}
    }
    
    // good
    const a = {
      defaults: {},
      common: {}
    }
    
  • 当使用动态属性名创建对象时,请使用对象计算属性名来进行创建

原因:因为这样做就可以让你在一个地方定义所有的对象属性

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 item = {
  value: 1,

  addValue: function (val) {
    return item.value + val
  }
}

// good
const item = {
  value: 1,

  addValue (val) {
    return item.value + val
  }
}

原因:这样更简短且描述更清楚

const job = 'FrontEnd'

// bad
const item = {
  job: job
}

// good
const item = {
  job
}
  • 将简写的对象属性分组后统一放到对象声明的开头

原因:这样更容易区分哪些属性用了简写的方式

const job = 'FrontEnd'
const department = 'JDC'

// bad
const item = {
  sex: 'male',
  job,
  age: 25,
  department
}

// good
const item = {
  job,
  department,
  sex: 'male',
  age: 25
}
  • 只对非法标识符的属性使用引号,eslint: quote-props

原因:因为通常来说我们认为这样主观上会更容易阅读,这样会带来代码高亮上的提升,同时也更容易被主流 JS 引擎优化

// bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5
}

// good
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5
}
  • 不要直接使用Object.prototype的方法, 例如hasOwnProperty,propertyIsEnumerableisPrototypeOf方法。

    原因:这些方法可能会被对象自身的同名属性覆盖 - 比如 { hasOwnProperty: false } 或者对象可能是一个 null 对象(Object.create(null))

// bad
console.log(object.hasOwnProperty(key))

// good
console.log(Object.prototype.hasOwnProperty.call(object, key))

// best
const has = Object.prototype.hasOwnProperty // cache the lookup once, in module scope.
console.log(has.call(object, key))
/* or */
import has from 'has' // https://www.npmjs.com/package/has
console.log(has(object, key))

3.4变量声明

  • 请使用字面量值创建数组,eslint: no-array-constructor

    // bad
    const items = new Array()
    
    // good
    const items = []
    
  • 向数组中添加元素时,请使用 push 方法

    const items = []
    
    // bad
    items[items.length] = 'test'
    
    // good
    items.push('test')
    
  • 使用数组的 map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return

3.5 解构赋值

  • 当需要使用对象的多个属性时,请使用解构赋值

愿意:解构可以避免创建属性的临时引用

const arr = [1, 2, 3, 4]

// bad
const first = arr[0]
const second = arr[1]

// good
const [first, second] = arr
  • 函数需要回传多个值时,请使用对象的解构,而不是数组的解构

原因:可以非破坏性地随时增加或者改变属性顺序

// bad
function doSomething () {
  return [top, right, bottom, left]
}

// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething()

// good
function doSomething () {
  return { top, right, bottom, left }
}

// 此时不需要考虑数据的顺序
const { top, left } = doSomething()

3.6 函数

  • 不要将参数命名为 arguments,会导致该参数的优先级高于每个函数作用域内原先存在的 arguments 对象
// bad
function foo (name, options, arguments) {
  // ...
}

// good
function foo (name, options, args) {
  // ...
}
  • 不要使用 arguments,使用 剩余运算符 ...

arguments 只是一个类数组,而 ... 是一个真正的数组

// bad
function test () {
  const args = Array.prototype.slice.call(arguments)
  return args.join('')
}

// good
function test (...args) {
  return args.join('')
}
  • 避免参数默认值的副作用
let b = 1
// bad
function count (a = b++) {
  console.log(a)
}
count()  // 1
count()  // 2
count(3) // 3
count()  // 3
  • 将参数默认值放在最后
// bad
function handleThings (opts = {}, name) {
  // ...
}

// good
function handleThings (name, opts = {}) {
  // ...
}

原因:参数重新赋值可能会导致无法预期的行为,尤其是当操作 arguments 对象时,也可能导致优化问题,尤其是在 V8 引擎中

// bad
function f1 (a) {
  a = 1
}

function f2 (a) {
  if (!a) { a = 1 }
}

// good
function f3 (a) {
  const b = a || 1
}

function f4 (a = 1) {
}
  • 调用可变参数函数时建议使用展开运算符 ...., eslint: prefer-spread

原因:显然你无需使用上下文,很难结合 newapply

// bad
const x = [1, 2, 3, 4, 5]
console.log.apply(console, x)

// good
const x = [1, 2, 3, 4, 5]
console.log(...x)

// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]))

// good
new Date(...[2016, 8, 5])

3.7 箭头函数

  • 当你必须使用函数表达式(传递匿名函数)时,使用箭头函数标记

原因:它将创建在 this 上下文中执行的函数版本,通常是您想要的,并且语法更简洁

如果您有一个相当复杂的函数,则可以将该逻辑移到其自己的命名函数表达式中

// 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, 否则保留花括号并使用 return 语句

3.8 类&构造函数

  • 使用 class,避免直接操作 prototype

  • 使用 extends 来实现继承

    原因:这是一个不会破坏 instanceof 的内建实现原型式继承的方式

  • 避免类成员重复

3.9 变量声明

  • 变量、函数在使用前必须先定义。

  • 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。

    变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。

  • 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。

    // good
    var hangModules = [];
    var missModules = [];
    var visited = {};
    
    // bad
    var hangModules = [],
        missModules = [],
        visited = {};
    
  • 原则上不建议使用全局变量,对于已有的全局变量或第三方框架引入的全局变量,需要根据检查工具的语法标识。

  • 声明变量时,请使用 constlet 关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用 const 来声明变量,我们需要避免全局命名空间的污染(不通过 var 定义变量将导致变量污染全局环境。)。

  • 变量不要进行链式赋值

    原因:变量链式赋值会创建隐藏的全局变量

  • 不允许出现未被使用的变量,声明但未被使用的变量通常是不完全重构犯下的错误.这种变量在代码里浪费空间并会给读者造成困扰

3.10 变量提升

  • var 存在变量提升的情况,即 var 声明会被提升至该作用域的顶部,但是他们的赋值并不会。而 constlet 并不存在这种情况

  • 匿名函数的变量名会提升,但函数内容不会

    function example () {
      console.log(anonymous)  // => undefined
    
      anonymous()
    
      var anonymous = function () {
        console.log('test')
      }
    }
    
  • 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会

3.11 比较运算符&相等

  • 使用 ===!== 而非 ==!=,使用 === 可以避免等于判断中隐式的类型转换。
  • 条件声明例如 if 会用 ToBoolean 这个抽象方法将表达式转成布尔值并遵循如下规则
    • Objects 等于 true
    • Strings 为 '' 时等于 false ,否则是 true
    • Numbers+0, -0, 或者 NaN 的情况下等于 false, 其他情况是 true
    • Booleans 等于 布尔值
    • Null 等于 false
    • Undefined 等于 false

3.12 eval()

  • 由于 eval 方法比较 evil,所以我们约定禁止使用该方法

3.13 修改内置对象的原型

  • 不要修改内置对象,如 ObjectArray

4. 代码规范

统一团队的编码规范,有助于代码的维护。本章是传统意义上的 Style Guideline,目的是统一一些相对主观化的代码风格。

  • 尽可能使用简洁的表达式

  • 对于相同变量或表达式的多值条件,用 switch 代替 if

  • 如果函数或全局中的 else 块后没有任何语句,可以删除 else

  • 不要在循环体中包含函数表达式,事先将函数提取到循环体外。

    循环体中的函数表达式,运行过程中会生成循环次数个函数对象。

  • [建议] 对有序集合进行顺序无关的遍历时,使用逆序遍历。

  • for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。

  • 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。

    自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序:

    1. 需要稳定的排序算法,达到严格一致的排序结果。
    2. 数据特点鲜明,适合使用桶排。
  • [建议] 清空数组使用 .length = 0

  • [建议] 一个函数的长度控制在 50 行以内。

    将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。

    特定算法等不可分割的逻辑允许例外。

  • 在适当的时候将闭包内大对象置为 null

  • 不在UI层做过量逻辑处理

  • v-if的使用条件、v-if和v-show的合理使用

  • 重复的UI结构使用循环创建

  • 计算属性和过滤器的合理使用

  • 大量的if判断 是否可以简写

    • 只有一层的【if()else()】,建议写成三目运算
    • 在多个条件判断时,需要在代码块中加入【return】或者写成连贯的【if(){}else if(){}else{}】
    //错误示例
    let i = 0;
    if (i == 0) {
      // 代码块
    }
    if (i == 1) {
      // 代码块
    }
    if (i == 2) {
      // 代码块
    }
    
    // 建议示例
    let i = 0;
    if (i == 0) {
      // 代码块
      return
    }
    if (i == 1) {
      // 代码块
      return
    }
    if (i == 2) {
      // 代码块
      return
    }
    // 或者
    if (i == 1) {
      // 代码块
    }else if (i == 2) {
      // 代码块
    }
    
  • 子组件中不做大量的业务逻辑处理

  • 父组件和子组件数据交互的方式

  • 数据类型定义

    • 如定义Number类型的变量,不允许赋予其他类型的值。
    • 变量初始化时,请不要定义为【null】,除定时器等特殊函数除外。
  • 代码中大量的重复代码,需提出单独封装

  • 代码中空函数,不使用的函数,因该删除或注释掉,避免占用内存空间(包括变量)

5. Vue 相关约束

5.1 Prop

5.1.1 Prop 定义应该尽量详细。

  • 在你提交的代码中,prop 的定义应该尽量详细,至少需要指定其类型。

细致的 prop 定义有两个好处:

  • 它们写明了组件的 API,所以很容易看懂组件的用法;
  • 在开发环境下,如果向一个组件提供格式不正确的 prop,Vue 将会告警,以帮助你捕获潜在的错误来源。

反例

// 这样做只有开发原型系统时可以接受
props: ['status']

好例子

props: {
  status: String
}
// 更好的做法!
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}

5.1.2 Prop 名大小写

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

我们单纯的遵循每个语言的约定。在 JavaScript 中更自然的是 camelCase。而在 HTML 中则是 kebab-case。

反例

props: {
  'greeting-text': String
}
<WelcomeMessage greetingText="hi"/>

好例子

props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>

5.1.3 多个Prop参数

在 JavaScript 中,用多行分隔对象的多个 property 是很常见的最佳实践,因为这样更易读。模板和 JSX 值得我们做相同的考虑。

反例

<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>

好例子

<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>

5.2 Mixin 混入

反例

var myGreatMixin = {
  // ...
  methods: {
    update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    _update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $_update: function () {
      // ...
    }
  }
}

好例子

var myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update: function () {
      // ...
    }
  }
}
// 甚至更好!
var myGreatMixin = {
  // ...
  methods: {
    publicMethod() {
      // ...
      myPrivateFunction()
    }
  }
}

function myPrivateFunction() {
  // ...
}

export default myGreatMixin

5.3 为 v-for 设置键值

总是用 key 配合 v-for

在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。甚至在元素上维护可预测的行为,比如动画中的对象固化 (object constancy),也是一种好的做法。

5.4 避免 v-ifv-for 用在一起

永远不要把 v-ifv-for 同时用在同一个元素上。

一般我们在两种常见的情况下会倾向于这样做:

  • 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ulol)。

当 Vue 处理指令时,v-forv-if 具有更高的优先级。哪怕我们只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。

计算属性上遍历,将会获得如下好处:

  • 过滤后的列表会在 users 数组发生相关变化时才被重新运算,过滤更高效。
  • 使用 v-for="user in activeUsers" 之后,我们在渲染的时候遍历活跃用户,渲染更高效。
  • 解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

反例

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

好例子

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

5.5 模板中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。

复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。

反例

{{
  fullName.split(' ').map(function (word) {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

好例子

<!-- 在模板中 -->
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}

5.6 简单的计算属性

应该把复杂计算属性分割为尽可能多的更简单的 property。

更简单、命名得当的计算属性是这样的:

  • 易于测试

    当每个计算属性都包含一个非常简单且很少依赖的表达式时,撰写测试以确保其正确工作就会更加容易。

  • 易于阅读

    简化计算属性要求你为每一个值都起一个描述性的名称,即便它不可复用。这使得其他开发者 (以及未来的你) 更容易专注在他们关心的代码上并搞清楚发生了什么。

  • 更好的“拥抱变化”

    任何能够命名的值都可能用在视图上。举个例子,我们可能打算展示一个信息,告诉用户他们存了多少钱;也可能打算计算税费,但是可能会分开展现,而不是作为总价的一部分。

    小的、专注的计算属性减少了信息使用时的假设性限制,所以需求变更时也用不着那么多重构了。

反例

computed: {
  price: function () {
    var basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}

好例子

computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}

6. 注释约定

  • 文件注释
<!--
 * @Author: 名称
 * @Date: 文件创建时间
 * @LastEditTime: 文件修改时间
 * @LastEditors: 修改人
 * @Description:  
 * @FilePath: 文件路径
 * @Copyright:版权
-->
/*
 * @Author: 名称
 * @Date: 文件创建时间
 * @LastEditTime: 文件修改时间
 * @LastEditors: 修改人
 * @Description:  
 * @FilePath: 文件路径
 * @Copyright:版权
**/
  • 函数注释
/**
 * 函数注释 
 * @param {*} i 形参注释
 * @return {*} 函数返回值注释
 */

7. 变量,函数名称约定

  • 请求接口相关,添加api前缀
  • 添加,追加相关的,添加add前缀
  • 设置相关的,添加set前缀
  • 获取相关的,添加get前缀
  • vue计算属性,添加Computed后缀