Vue的编码规范

原文地址:https://zhuanlan.zhihu.com/p/25654116

一、基于模块开发

始终基于模块的方式来构建你的 app,每一个子模块只做一件事情。

Vue.js 的设计初衷就是帮助开发者更好的开发界面模块。一个模块是应用程序中独立的一个部分。

怎么做?

每一个 Vue 组件(等同于模块)首先必须专注于解决一个单一的问题,独立的, 可复用的, 微小的 and 可测试的。

如果你的组件做了太多的事或是变得臃肿,请将其拆分成更小的组件并保持单一的原则。一般来说,尽量保证每一个文件的代码行数不要超过 100 行。也请保证组件可独立的运行。比较好的做法是增加一个单独的 demo 示例。

二、Vue 组件命名

组件的命名需遵从以下原则:

  1. 有意义的: 不过于具体,也不过于抽象
  2. 简短: 2 到 3 个单词
  3. 具有可读性: 以便于沟通交流

同时还需要注意:

  1. 必须符合自定义元素规范: 使用连字符分隔单词,切勿使用保留字。
  2. app- 前缀作为命名空间: 如果非常通用的话可使用一个单词来命名,这样可以方便于其它项目里复用。
为什么?

组件是通过组件名来调用的。所以组件名必须简短、富有含义并且具有可读性。
如何做?

<!-- 推荐 -->
<app-header></app-header>
<user-list></user-list>
<range-slider></range-slider>

<!-- 避免 -->
<btn-group></btn-group> <!-- 虽然简短但是可读性差. 使用 `button-group` 替代 -->
<ui-slider></ui-slider> <!-- ui 前缀太过于宽泛,在这里意义不明确 -->
<slider></slider> <!-- 与自定义元素规范不兼容 -->

三、组件表达式简单化

Vue.js 的表达式是 100% 的 Javascript 表达式。这使得其功能性很强大,但也带来潜在的复杂性。因此,你应该尽量保持表达式的简单化。

为什么?
  1. 复杂的行内表达式难以阅读。
  2. 行内表达式是不能够通用的,这可能会导致重复编码的问题。
  3. IDE 基本上不能识别行内表达式语法,所以使用行内表达式 IDE 不能提供自动补全和语法校验功能。
怎么做?

如果你发现写了太多复杂并难以阅读的行内表达式,那么可以使用 method 或是 computed 属性来替代其功能。

<!-- 推荐 -->
<template>
    <h1>
        {{ `${year}-${month}` }}
    </h1>
</template>
<script type="text/javascript">
  export default {
    computed: {
      month() {
        return this.twoDigits((new Date()).getUTCMonth() + 1);
      },
      year() {
        return (new Date()).getUTCFullYear();
      }
    },
        methods: {
      twoDigits(num) {
        return ('0' + num).slice(-2);
      }
    },
  };
</script>
<!-- 避免 -->
<template>
    <h1>
        {{ `${(new Date()).getUTCFullYear()}-${('0' + ((new Date()).getUTCMonth()+1)).slice(-2)}` }}
    </h1>
</template>

四、组件 props 原子化

虽然 Vue.js 支持传递复杂的 JavaScript 对象通过 props 属性,但是你应该尽可能的使用原始类型的数据。尽量只使用JavaScript 原始类型(字符串、数字、布尔值) 和 函数。尽量避免复杂的对象。

为什么?
  1. 使得组件 API 清晰直观。
  2. 只使用原始类型和函数作为 props 使得组件的 API 更接近于 HTML(5) 原生元素。
  3. 其它开发者更好的理解每一个 prop 的含义,作用。
  4. 传递过于复杂的对象使得我们不能够清楚的知道哪些属性或方法被自定义组件使用,这使得代码难以重构和维护。
怎么做?

组件的每一个属性单独使用一个 props,并且使用函数或是原始类型的值。

<!-- 推荐 -->
<range-slider
  :values="[10, 20]"
  min="0"
  max="100"
  step="5"
  :on-slide="updateInputs"
  :on-end="updateResults">
</range-slider>

<!-- 避免 -->
<range-slider :config="complexConfigObject"></range-slider>

五、验证组件的 props

在 Vue.js 中,组件的 props 即 API,一个稳定并可预测的 API 会使得你的组件更容易被其他开发者使用。

组件 props 通过自定义标签的属性来传递。属性的值可以是 Vue.js 字符串(:attr=“value” 或 v-bind:attr=“value”)或是不传。你需要保证组件的 props 能应对不同的情况。

为什么?

验证组件 props 可以保证你的组件永远是可用的(防御性编程)。即使其他开发者并未按照你预想的方法使用时也不会出错。

怎么做?
  1. 提供默认值
  2. 使用 type 属性校验类型
  3. 使用 props 之前先检查该 prop 是否存在
<template>
  <input type="range" v-model="value" :max="max" :min="min">
</template>
<script type="text/javascript">
  export default {
    props: {
      max: {
        type: Number, // 这里添加了数字类型的校验
        default() { return 10; },
      },
      min: {
        type: Number,
        default() { return 0; },
      },
      value: {
        type: Number,
        default() { return 4; },
      },
   },
 };
</script>

六、将 this 赋值给 component 变量

在 Vue.js 组件上下文中,this指向了组件实例。因此当你切换到了不同的上下文时,要确保 this 指向一个可用的 component 变量。

换句话说,不要在编写这样的代码 const self = this; ,而是应该直接使用变量 component。

为什么?

将组件 this 赋值给变量 component可用让开发者清楚的知道任何一个被使用的地方,它代表的是组件实例。

怎么做?
<script type="text/javascript">
export default {
  methods: {
    hello() {
      return 'hello';
    },
    printHello() {
      console.log(this.hello());
    },
  },
};
</script>

<!-- 避免 -->
<script type="text/javascript">
export default {
  methods: {
    hello() {
      return 'hello';
    },
    printHello() {
      const self = this; // 没有必要
      console.log(self.hello());
    },
  },
};
</script>

七、组件结构化

顺序:大致按照这个顺序书写组件完整的属性(用的才写,没用到的没必要写)。name、components、beforeCreate、props、data、computed、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroy、destroyed

<!-- 示例 -->
<template>
	<div>
		<!-- vue推荐方式 -->
		<user-list></user-list>
	</div>
</template>
<script type="text/ecmascript-6">
	import引入原则:
    // import { mapState, mapGetters, mapActions, mapMutations } form 'vuex' // 涉及到状态管理的组件优先引入vuex

    // import component from 'components/xxxxxx.component' // components

    // import directive from 'directives/xxxxxx.directive' // directives

    // import { method_1, method_2 } from 'sdk/xxxxx' // 方法/工具/接口

    // 以上的components | directives | sdk等等为简写形式,具体规则参见bulid/webpack.base.conf.js文件中的alias配置.此配置是为了统一引入规则,并以防项目文件迁移的时候每个引入路径都需要改动,设置alias之后只需改动该配置项即可

    属性及方法书写原则:
    // vue作为一个数据驱动的框架,数据是主体,所有和数据相关的属性应该尽量放置在靠前的位置(主要包括data, props, computed)

    export default {
    
	 // 不要忘记了 name 属性
    	name: 'RangeSlider',
    	
     // 组合其它组件
    	extends: {},
    	
    // 组件属性
        props:{
            bar: {}, // 按字母顺序
            foo: {},
            fooBar: {},
        }, // 若有外部传入参数,优先书写props.排序原因:props是整个组件的数据基础,作为一个先决条件应该被优先依赖

 		// 变量
 		
        data () {}, // data中存放有关该组件自身内部控制状态或数据的信息.排序原因:data中的数据可以依赖于props中的数据.
        computed () {}, // 此属性的应用场景多为props或data中的数据并非展示到页面上的最终数据,需要进行一定的格式化或者计算,如props中date值为ms数,而页面中的倒计时需转换为[时:分:秒]形式,此时需用到计算属性.排序原因:computed属性可以依赖于props或data中的数据,并可随数据源的变化进行同步更新.

    // 组件及外部引入相关:

        components: {}, // 此属性为该组件内部对于其他组件的依赖,一般占位不多,放在尽量靠前的位置方便增删以及修改.
        directives: {}, // 同上

    // 钩子函数:
        beforeCreate () {}, // 较少用到该钩子函数,有待发掘
        created () {}, // 此函数执行的时机为props,data,computed等数据初始化完毕之后,此时vue实例并没有挂载到dom上,所以切记,不要在此阶段进行dom操作.通常在此阶段调用接口数据.
        beforeMounted () {}, // vue实例挂载之前的钩子函数,较少用到,能在此阶段调用的通常可在created阶段执行
        mounted () {}, // vue实例挂载到dom上,在此阶段可以使用$el,$refs等获取dom元素.尽量避免使用选择器来对dom进行操作.(作为数据驱动的框架,原则上能不动dom就不动,除非在不得不动的情况下再考虑操作dom).
        beforeUpdate () {}, // 数据更新之前调用,也就是每次数据更新都会执行该函数,其调用时机的高频性注定其使用的低频性.所以,项目中几乎不会用到.
        updated () {}, // 数据更新之后调用,使用情况同上
        beforeDestroy () {}, // 不明所以,不知有什么合理的使用时机和情况
        destroyed () {}, // 同上

    // 其他属性及方法:

        watch: {}, // 当我们需要根据一个数据的变化来实现其他部分的同步更改的情况下,会使用的此属性,检测其变化,并根据变化值来确定视图或者数据的最终形态.
        methods: {} // 所有涉及到方法/操作/触发等动作的,无论同步还是异步,都应在此处体现出来,以保证其可追溯性.是构成整个组件的逻辑操作的主体,通常所占篇幅较大,建议放在最后,以避免将其他属性和方法挤到最后不方便查看,且其更似一个功能独立的单块.
    }
</script>

八、避免 this.$parent

Vue.js 支持组件嵌套,并且子组件可访问父组件的上下文。访问组件之外的上下文违反了基于模块开发的第一原则。因此你应该尽量避免使用 this.$parent。

为什么?
  1. 组件必须相互保持独立,Vue 组件也是。如果组件需要访问其父层的上下文就违反了该原则。
  2. 如果一个组件需要访问其父组件的上下文,那么该组件将不能再其它上下文中复用。
怎么做?
  1. 通过 props 将值传递给子组件
  2. 通过 props 传递回调函数给子组件来达到调用父组件方法的目的
  3. 通过在子组件触发事件来通知父组件

九、谨慎使用 this.$refs

Vue.js 支持通过 ref 属性来访问其它组件和 HTML 元素。并通过 this. r e f s 可 以 得 到 组 件 或 H T M L 元 素 的 上 下 文 。 在 大 多 数 情 况 下 , 通 过 t h i s . refs 可以得到组件或 HTML 元素的上下文。在大多数情况下,通过 this. refsHTMLthis.refs来访问其它组件的上下文是可以避免的。在使用的的时候你需要注意避免调用了不恰当的组件 API,所以应该尽量避免使用 this.$refs。

为什么?
  1. 组件必须是保持独立的,如果一个组件的 API 不能够提供所需的功能,那么这个组件在设计、实现上是有问题的。
  2. 组件的属性和事件必须足够的给大多数的组件使用
怎么做?
  1. 提供良好的组件 API
  2. 总是关注于组件本身的目的
  3. 拒绝定制代码。如果你在一个通用的组件内部编写特定需求的代码,那么代表这个组件的 API 不够通用,或者你可能需要一个新的组件来应对该需求
  4. 检查所有的 props 是否有缺失的,如果有提一个 issue 或是完善这个组件
  5. 检查所有的事件。子组件向父组件通信一般是通过事件来实现的,但是大多数的开发者更多的关注于 props 从忽视了这点。
  6. Props向下传递,事件向上传递!。以此为目标升级你的组件,提供良好的 API 和 独立性。
  7. 当遇到 props 和 events 难以实现的功能时,通过 this.$refs来实现。
  8. 当需要操作 DOM 无法通过指令来做的时候可使用 this…$ref 而不是 JQuery, document.getElement*
    , document.queryElement。
<!-- 推荐,并未使用 this.$refs -->
<range :max="max"
  :min="min"
  @current-value="currentValue"
  :step="1"></range>
<!-- 使用 this.$refs 的适用情况-->
<modal ref="basicModal">
  <h4>Basic Modal</h4>
  <button class="primary" @click="$refs.basicModal.close()">Close</button>
</modal>
<button @click="$refs.basicModal.open()">Open modal</button>

<!-- Modal component -->
<template>
  <div v-show="active">
    <!-- ... -->
  </div>
</template>
<script>
  export default {
    // ...
    data() {
        return {
            active: false,
        };
    },
    methods: {
      open() {
        this.active = true;
      },
      hide() {
        this.active = false;
      },
    },
    // ...
  };
</script>
<!-- 如果可通过 emited 来做则避免通过 this.$refs 直接访问 -->
<template>
  <range :max="max"
    :min="min"
    ref="range"
    :step="1"></range>
</template>

<script>
  export default {
    // ...
    methods: {
      getRangeCurrentValue() {
        return this.$refs.range.currentValue;
      },
    },
    // ...
  };
</script>

十、使用组件名作为样式作用域空间

Vue.js 的组件是自定义元素,这非常适合用来作为样式的根作用域空间。可以将组件名作为 css 类的命名空间。

为什么?

  1. 给样式加上作用域空间可以避免组件样式影响外部的样式
  2. 保持模块名、目录名、样式根作用域名一样,可以很好的将其关联起来,便于开发者理解。

怎么做?
使用组件名作为样式命名的前缀,可基于 BEM 或 OOCSS 范式。同时给style标签加上 scoped 属性。加上 scoped 属性编译后会给组件的 class 自动加上唯一的前缀从而避免样式的冲突。

<style scoped>
    /* 推荐 */
    .MyExample { }
    .MyExample li { }
    .MyExample__item { }

    /* 避免 */
    .My-Example { } /* not scoped to component or module name, not BEM compliant */
</style>

十一、ESLint

ESLint 需要通过 ESLint HTML 插件来抽取组件中的代码。

通过 .eslintrc 文件来配置 ESlint,这样 IED 可以更好的理解校验配置项。 ESlint,这样.

{
  "extends": "eslint:recommended",
  "plugins": ["html"],
  "env": {
    "browser": true
  },
  "globals": {
    "opts": true,
    "vue": true
  }
}

运行 ESLint

eslint src/**/*.vue
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值