前端代码规范(Vue、JavaScript、CSS、HTML、代码Commite相关规范)


前言

该文章旨在增强团队开发协作、提高代码质量和打造开发基石的编码规范。

一、Vue风格指南

组件名为多个单词

  • 可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的 详情
// bad
app.component('todo', {
  // ...
})

// good
app.component('todo-item', {
  // ...
})

Prop 定义应尽量详细

  • prop 的定义应该尽量详细,至少需要指定其类型 详情
// bad
props: ['status']

// good
props: {
  status: String
}

// better
props: {
  status: {
    type: String,
    required: true,
    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

为 v-for 设置 key 值

  • 在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态 详情
<!-- bad -->
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

<!-- good -->
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

避免 v-if 和 v-for 一起使用

  • 永远不要把 v-ifv-for 同时用在同一个元素上 详情
<!-- bad -->
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

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

为组件样式设置作用域

  • 设置一致的作用域会确保样式只会运用在想要作用的组件上 详情
<!-- bad -->
<style>
.btn-close {
  background-color: red;
}
</style>

<!-- good -->
<!-- 使用 `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}
</style>

模板中的简单表达式

  • 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法 详情
<!-- bad -->
{{
  fullName.split(' ').map((word) => {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

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

指令缩写

  • 指令缩写 (用 : 表示 v-bind:@ 表示 v-on: 和用 # 表示 v-slot) 应该要么都用要么都不用 详情
<!-- bad -->
<input
  v-bind:value="newTodoText"
  @focus="onFocus"
>

<!-- good -->
<input
  :value="newTodoText"
  @focus="onFocus"
>

组件/实例的选项顺序

  • 组件/实例的选项应该有统一的顺序 详情
  1. 全局感知 (要求组件以外的知识)

    name

  2. 模板依赖 (模板内使用的资源)

    components

    directives

  3. 组合 (向选项里合并 property)

    extends

    mixins

    provide / inject

  4. 接口 (组件的接口)

    inheritAttrs

    props

    emits

  5. 组合式 API (使用组合式 API 的入口点)

    setup

  6. 本地状态 (本地的响应式 property)

    data

    computed

  7. 事件 (通过响应式事件触发的回调)

    watch

    生命周期钩子 (按照它们被调用的顺序)

    • beforeCreate
    • created
    • beforeMount
    • mounted
    • beforeUpdate
    • activated
    • deactivated
    • beforeUnmount
    • unmounted
    • errorCaptured
    • renderTracked
    • renderTriggered
  8. 非响应式的 property (不依赖响应性系统的实例 property)

    methods

  9. 渲染 (组件输出的声明式描述)

    template / render

更多内容

二、JS规范

1. 引用

  • 请记得 constlet 都是块级作用域var函数级作用域
    {
      let a = 1
      const b = 1
    }
    console.log(a) // ReferenceError
    console.log(b) // ReferenceError
    
  • 对所有引用都使用 const,不要使用 var
    // bad
    var a = 1
    var b = 2
    
    // good
    const a = 1
    const b = 2
    
  • 如果引用是可变动的,使用 let 代替 var
    // bad
    var count = 1
    if (count < 10) {
      count += 1
    }
    
    // good
    let count = 1
    if (count < 10) {
      count += 1
    }
    

2. 对象

  • 请使用字面量值创建对象;

    // bad
    const a = new Object{}
    
    // good
    const a = {}
    
  • 当使用动态属性名创建对象时,请使用对象计算属性名来进行创建;

    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
    }
    
  • 只对非法标识符的属性使用引号;

    // bad
    const bad = {
      'foo': 3,
      'bar': 4,
      'data-blah': 5
    }
    
    // good
    const good = {
      foo: 3,
      bar: 4,
      'data-blah': 5
    }
    
  • 优先使用对象展开运算符 ... 来做对象浅拷贝而不是使用 Object.assign,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象。

    // very bad
    const original = { a: 1, b: 2 }
    const copy = Object.assign(original, { c: 3 }) // this mutates `original` ಠ_ಠ
    delete copy.a // so does this
    
    // bad
    const original = { a: 1, b: 2 }
    const copy = Object.assign({}, original, { c: 3 }) // copy => { a: 1, b: 2, c: 3 }
    
    // 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 }
    

3. 数组

  • 请使用字面量值创建数组
    // bad
    const items = new Array()
    
    // good
    const items = []
    
  • 向数组中添加元素时,请使用 push 方法
    const items = []
    
    // bad
    items[items.length] = 'test'
    
    // good
    items.push('test')
    
  • 使用展开运算符 … 复制数组
    // bad
    const items = []
    const itemsCopy = []
    const len = items.length
    let i
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i]
    }
    
    // good
    itemsCopy = [...items]
    
  • 使用数组的 map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return
    // good
    [1, 2, 3].map(x => {
      const y = x + 1
      return x * y
    })
    
    // good
    [1, 2, 3].map(x => x + 1)
    
    // bad
    const flat = {}
    [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
      const flatten = memo.concat(item)
      flat[index] = flatten
    })
    
    // good
    const flat = {}
    [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
      const flatten = memo.concat(item)
      flat[index] = flatten
      return flatten
    })
    

4. 解构赋值

  • 当需要使用对象的多个属性时,请使用解构赋值
    // 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}`
    }
    
    // better
    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
    
  • 函数需要回传多个值时,请使用对象的解构,而不是数组的解构
    // 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()
    

5. 字符串

  • 字符串统一使用单引号的形式 '
    // bad
    const department = "JDC"
    
    // good
    const department = 'JDC'
    
  • 字符串太长的时候,请不要使用字符串连接符换行 \,而是使用 +
    const str = '前端代码规范 前端代码规范 前端代码规范' +
      '前端代码规范 前端代码规范 前端代码规范' +
      '前端代码规范 前端代码规范'
    
  • 程序化生成字符串时,请使用模板字符串
    const test = 'test'
    
    // bad
    const str = ['a', 'b', test].join()
    
    // bad
    const str = 'a' + 'b' + test
    
    // good
    const str = `ab${test}`
    

6. 函数

  • 不要使用 arguments,使用 剩余运算符 ...
    // bad
    function test () {
      const args = Array.prototype.slice.call(arguments)
      return args.join('')
    }
    
    // good
    function test (...args) {
      return args.join('')
    }
    
  • 使用参数默认值语法而不是修改函数参数
    // really bad
    function handleThings (opts) {
      // No! We shouldn't mutate function arguments.
      // Double bad: if opts is falsy it'll be set to an object which may
      // be what you want but it can introduce subtle bugs.
      opts = opts || {}
      // ...
    }
    
    // still bad
    function handleThings (opts) {
      if (opts === void 0) {
        opts = {}
      }
      // ...
    }
    
    // good
    function handleThings (opts = { }) {
      // ...
    }
    
  • 将参数默认值放在最后
    // bad
    function handleThings (opts = {}, name) {
      // ...
    }
    
    // good
    function handleThings (name, opts = {}) {
      // ...
    }
    
  • 不要更改参数或重新赋值
    // 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) {
    }
    

7. 模块

  • 使用标准的 ES6 模块语法 importexport
    // bad
    const util = require('./util')
    module.exports = util
    
    // good
    import Util from './util'
    export default Util
    
    // better
    import { Util } from './util'
    export default Util
    
  • 同个文件每个模块只允许 import 一次,有多个 import 请书写在一起
    // bad
    import foo from 'foo'
    // … some other imports … //
    import { named1, named2 } from 'foo'
    
    // good
    import foo, { named1, named2 } from 'foo'
    
    // good
    import foo, {
      named1,
      named2
    } from 'foo'
    
  • 将所有 import 语句放在文件最前方
    // bad
    import foo from 'foo'
    foo.init()
    
    import bar from 'bar'
    
    // good
    import foo from 'foo'
    import bar from 'bar'
    
    foo.init()
    
  • 多行导入应该像多行数组和对象文字一样缩进
    // bad
    import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path'
    
    // good
    import {
      longNameA,
      longNameB,
      longNameC,
      longNameD,
      longNameE
    } from 'path'
    

8. 对象属性

  • 使用 .来访问对象属性
    const joke = {
      name: 'haha',
      age: 28
    }
    
    // bad
    const name = joke['name']
    
    // good
    const name = joke.name
    
  • 当访问的属性是变量时使用 []
    const luke = {
      jedi: true,
      age: 28,
    }
    
    function getProp (prop) {
      return luke[prop]
    }
    
    const isJedi = getProp('jedi')
    

9. 变量声明

  • 声明变量时,请使用 constlet 关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么
    // bad
    demo = new Demo()
    
    // good
    const demo = new Demo()
    
  • 将所有的 constlet 分组
    // bad
    let a
    const b
    let c
    const d
    let e
    
    // good
    const b
    const d
    let a
    let c
    let e
    

10. 比较运算符&相等

  • 使用 ===!== 而非 ==!=

更多内容

三. css规范

1. 属性书写顺序

  1. 布局定位属性:display / position / float / clear / visibility / overflow
  2. 自身属性:width / height / margin / padding / border / background
  3. 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
  4. 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient …
    .jdc {
        display: block;
        position: relative;
        float: left;
        width: 100px;
        height: 100px;
        margin: 0 10px;
        padding: 20px 0;
        font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
        color: #333;
        background: rgba(0,0,0,.5);
        -webkit-border-radius: 10px;
        -moz-border-radius: 10px;
        -o-border-radius: 10px;
        -ms-border-radius: 10px;
        border-radius: 10px;
    }
    

2. 代码大小写

  • 样式选择器,属性名,属性值关键字全部使用小写字母书写
    /* 推荐 */
    .jdc{
    	display:block;
    }
    	
    /* 不推荐 */
    .JDC{
    	DISPLAY:BLOCK;
    }
    

3. 选择器

  • 尽量少用通用选择器 *
  • 不使用 ID 选择器
  • 不使用无具体语义定义的标签选择器
    /* 推荐 */
    .jdc {}
    .jdc li {}
    .jdc li p{}
    
    /* 不推荐 */
    *{}
    #jdc {}
    .jdc div{}
    

4. 代码易读性

  • 属性值十六进制数值能用简写的尽量用简写
    /* 推荐 */
    .jdc {
        color: #fff;
    }
    
    /* 不推荐 */
    .jdc {
        color: #ffffff;
    }
    
  • 不要为 0 指明单位
    /* 推荐 */
    .jdc {
        margin: 0 10px;
    }
    
    /* 不推荐 */
    .jdc {
        margin: 0px 10px;
    }
    

5. 变量

  • 可复用属性尽量抽离为页面变量,易于统一维护
    // CSS
    .jdc {
        color: red;
        border-color: red;
    }
    
    // SCSS
    $color: red;
    .jdc {
        color: $color;
        border-color: $color;
    }
    

更多内容

四. HTML规范

1. HTML代码大小写

  • HTML标签名、类名、标签属性和大部分属性值统一用小写
    <!-- 推荐 -->
    <input type="text">
    <input type="radio" name="name" checked="checked" >
    
    <!-- 不推荐 -->
    <input type=text>	
    <input type='text'>
    <input type="radio" name="name" checked >
    

2. 元素属性

  • 元素属性值使用双引号语法
  • 元素属性值可以写上的都写上
    <!-- 推荐 -->
    <div class="demo"></div>
    
    <!-- 不推荐 -->
    <div class="DEMO"></div>
    <DIV CLASS="DEMO"></DIV>
    

3. 纯数字输入框

  • 使用 type="tel" 而不是 type="number"
    <input type="tel">
    

4. 代码嵌套

  • 元素嵌套规范,每个块状元素独立一行,内联元素可选
    <!-- 推荐 -->
    <div>
        <h1></h1>
        <p></p>
    </div>	
    <p><span></span><span></span></p>
    
    <!-- 不推荐 -->
    <div>
        <h1></h1><p></p>
    </div>	
    <p> 
        <span></span>
        <span></span>
    </p>
    
  • 段落元素与标题元素只能嵌套内联元素
    <!-- 推荐 -->
    <h1><span></span></h1>
    <p><span></span><span></span></p>
    
    <!-- 不推荐 -->
    <h1><div></div></h1>
    <p><div></div><div></div></p>
    

更多内容

五. 代码Commite规范

格式

Commit 规范采用常用的 Angular 团队所使用的规范,具体如下:


<type><scope>: <subject>
<空行>
<body>
<空行>
<footer>

type 规则(必填)

type 代表本次 commit 的类型,有且仅有如下几种:

  • feat - 功能性更新
  • fix - bug 修复
  • style - 改变代码格式(如删除空行、格式化代码、去除不必要的分号等等)
  • refactor - 既不是功能更新也不是 bug 修复的更改(建议对代码进行重构的时候使用)
  • perf - 代码改变提高了性能
  • test - 添加测试用例或者修改测试用例
  • build - 由打包工具造成的改变(如gulp、webpack编译文件)
  • chore - 既不是源码的修改,也不是测试用例的修改(修改项目相关配置时可以使用)
  • revert - 撤销之前的提交

scope 规则(必填)

scope 代表本次 commit 的影响范围,暂定规则如下:

  • 本次 commit 修改的组件
  • 本次 commit 修改的文件
  • 本次 commit 修改的文件夹

subject 规则(必填)

用一句简短的话描述本次修改的内容,不要超过30个汉字,以动词开头

建议选用如下动词:

  • 新增(组件、属性、事件、API)
  • 删除
  • 修正
  • 修复
  • 修改

body 规则(选填)

如果 subject 无法对本次 commit 进行清楚的阐释,则在 body 中进行补充说明。

建议填写以下内容:

  • 为什么进行本次修改
  • 本次修改了哪些内容
  • 修改后的影响有哪些

footer 规则(选填)

footer 中只填写两种内容:

  1. 这次 commit 和某个 issue 相关联,提交后能关闭该 issue,则填写:
    
    close #748
    
    
  2. 这次 commit 有不兼容上个版本的代码,则以BREAKING CHANGE:开头填写不兼容信息,如下:
    
    BREAKING CHANGE: Message组件top属性单位由px改为rpx
    
    

示例

一个完整规范且正确的 Commit 示例如下:


fix(NoticeBar):修改top属性单位为rpx

NoticeBar组件的top属性单位之前为px,会出现无法自适应的问题。
更改为rpx后可对屏幕进行自适应。

BREAKING CHANGE: Notice-Bar组件top属性单位由px改为rpx

Close #745

错误示例

  1. subject描述中出现组件名称
    
    feat(Button): Button 组件新增 size 属性
    
    
    因type(括号中的内容)已经指定了组件,所以 subject 描述信息中无需再指明组件
  2. 单词未添加空格
    
    feat(Button): 新增size属性
    
    
    为了生成的 changelog 的可读性,所有的单词左右都需要添加一个空格

其他事项

一个 commit 应该是一个有意义的 commit

有意义的定义如下:

  • 新增了一个功能或组件
  • 修复了一个bug
  • 解决了一个issue
  • 重构了某个组件或文件
  • 改善了现有代码的构建流程或风格

无意义的定义如下:

  • 临时工作进度保存
  • 误提交的 commit
  • commit 信息不规范或缺失
  • subject 无法准确描述此次 commit

更多内容

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值