前端编码规范

我是图片

前言

学习了业界一些对于编码风格的理论,结合一些业界比较推荐的规范风格,反观自身的编码习惯,总结出这么一份自己比较喜欢的规范风格。

html

html标签中有很多属性,这些属性假如没有一定的排列规则,会现得很乱,不整洁。以vue tempalte的html为例,我推荐一下属性排列优先级:

  1. id,ref,class等属于标签的标识排前
  2. v-if,v-for等操作类的属性
  3. v-model和带绑定的属性
  4. 无绑定的属性
  5. 事件绑定

另外属性个数大于三个要换行显示

1
2
3
4
5
6
7
8
9
10
<Tag 
ref="tag"
v-for="(item,index) in tabsInfo"
:title="item.title"
:link="item.router"
:active="item.active"
:key="index"
palcehold="请输入"
@click="handleClick"
><Tag>

css

业界比较推荐的一种规范是BEM(块级元素修饰符),它可以让你的css模块化,变得易维护。

js

命名

计算机科学只存在两个难题:缓存失效和命名。 –Phil KarIton

命名一直是计算机科学里的一大难题,在名变量时我只要做到准确、名副其实就可以了,记住一点就是要做到让人阅读代码就像是阅读一本书,用代码述说故事。

在命名是我比较喜欢参照一下几点原则:

  • 命名变量使用小写字母加下划线,这样是为了跟函数的小驼峰命名做区分,好让我一样就知道这是变量,而且使用名词或名词短语
  • 命名常量使用大写字母加下划线
  • 命名函数使用小驼峰,使用动词或动词短语,表示动作
  • 命名类使用就大驼峰

函数

函数的第一规则是短小,第二规则是还要更短小。 –《代码整洁之道》

写函数我一般遵循以下几点原则:

  • 短小。假如函数过长,你就要做拆分,最好不要超过20行。
  • if else语句不要嵌套太深,最多两层。
  • 好的函数只做一件事情,也就是单一功能原则,并且能用函数名表达函数的主要功能。这里有个技巧,就是函数越短小,功能越集中,就越能取个准确的命名。
  • 参数不要过多,最好三个之内,三个以上要有足够的理由。
  • 减少副作用。
  • 错误提前返回
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function observe(obj) {
    if (obj && typeof obj === 'object') {
    Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key])
    })
    }
    }
    改为:
    function observe(obj) {
    if (!obj || typeof obj !== 'object') return

    Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key])
    })
    }
  • 自上而下排版函数。类似读报纸一样,细节总上在一个段落的下面。函数内的函数就类比细节,把内函数声明的位置放在外函数下面,依次排列。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function A() {
    B()
    C()
    }
    // 以下都是A的细节
    function B() {
    D()
    }
    --// D属于B的细节,放在B下面
    function D() {}

    function C() {
    E()
    }
    --// E属于C的细节,放在C下面
    function E() {}
  • 公共API函数要写注释。以下摘自tj大神co库里的一段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * Wrap the given generator `fn` into a
    * function that returns a promise.
    * This is a separate function so that
    * every `co()` call doesn't create a new,
    * unnecessary closure.
    *
    * @param {GeneratorFunction} fn
    * @return {Function}
    * @api public
    */

    co.wrap = function (fn) {
    createPromise.__generatorFunction__ = fn;
    return createPromise;
    function createPromise() {
    return co.call(this, fn.apply(this, arguments));
    }
    };

如何能写出好函数呢?首先要记住好代码是改出来,好函数也一样,所以先把函数的主体逻辑写好,再参照规则改造就好了。

注释

“别给糟糕的代码添加注释,重新写吧。” –《代码整洁之道》

《代码整洁之道》这本书不建议我们给表意不明的代码添加注释。总结的理由如下:

  1. 好代码可以从函数名就知道意思,假如函数名表达不了代码意图,就需要改造代码。
  2. 注释是人写的,会有不准确性,有误人子弟的可能。
  3. 写注释的原因可能是代码难读,一遇到难读的代码就写注释会导致跟多难读的代码。

但是所有的注释都不应该写吗?肯定不是。

注释也分类型,没必要写的类型有:

  1. 喃喃自语类的注释,表意不明的信息。
  2. 多余的说明。这种注释像是翻译一遍函数名。
  3. 误导性的注释,就是不准确的描述了函数的意图。
  4. 评论类的注释。对代码的评论可以使用github的评论工具,没必要写在代码里。
  5. 骂人的注释。。

有必要写注释的类型有:

  1. 法律信息和开源信息类,或者是一个库开头的说明信息。
    1
    2
    3
    4
    5
    /*!
    * Vue.js v2.6.10
    * (c) 2014-2019 Evan You
    * Released under the MIT License.
    */
  2. 说明在某种场景,某种情况的注释,这种注释就是为了说明因为某种特殊的原因才有以下代码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    // 当B网站的api data更新时会通过storage通知这里
    // 监听storage,实时更新api data
    window.addEventListener("storage", (event) => {
    console.warn('event', event);
    const { key, newValue } = event;
    const api_storage_key = `api_list_${this.tpl_config.app_name}`;
    if (key === api_storage_key) {
    console.log("storage change,key ", key)
    this.$store.commit("initApi", {
    data: JSON.parse(newValue).content || []
    })
    if (this.select_node.label === "Form") {
    const api_name = this.props_temp.model.api
    if (!api_name) return

    const api = this.apilist.find(api => api.name === api_name);
    // 需要更新当前Form面板关联api的参数配置
    this.createFormContentWithApiConf(api)
    }
    }
    })
    以上代码说明了场景当B站点更新api data时,会以storage的方式通知,这就监听storage代码出现的原因。
  3. 对意图的解析。比如函数内部的某一段为什么这么写,是什么原因造成这样写的决定。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    renameComponent(name) {
    switch (name) {
    // Switch不能作为组件名注册
    case 'SwitchRadio':
    name = 'Switch'
    break
    }
    return name
    },
  4. 对于特殊常量的说明。
    1
    const TYPE_FORM = 0; // 上搜下标类模板
  5. TODO注释
    1
    TODO: 太慢,待优化!
  6. 公共API的注释
  7. 注释掉代码,有必要写明注释掉的原因,好让人知道改删掉还是不改删掉。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for (let key in defaultData) {
    this.props_temp[prop].relatedData[key] = {
    label: key,
    prop: key,
    // 暂时去掉默认值
    // value: defaultData[key],
    inputType: "Input"
    };
    }