手记

Vue组件开发小结

组件化是长期开发过程中一个提炼精华的过程,目的主要是以下几点:

  1. 提高复用性

  2. 解耦

  3. 提升未来的开发效率

那么如何达到这样的效果呢,我们可以分几步来循序渐进地完成。

下文主要是思路,想直接获取代码可转战ElementUI的Github去看源码

一 组件的定义

组件从大类上可以分为两种:

  • 基础组件:例如ElementUI

  • 业务组件:通过基础组件或者业务组件组合而成,与业务强相关甚至强绑定

二 组件的颗粒度

基础组件的颗粒度争议并不大,就是Button,Table等等
业务组件的颗粒度最大可以为一个feature,一个feature就是一个可以独立上线特性,例如文章的评论点赞功能。我们可以想象有一个开关,打开就有这个feature,关闭就没有,且不会造成联动的影响。

三 组件的接口

  1. Vue的组件基本都是通过属性来进行配置,进而控制组件的功能变化。因此开发组件之前我们就得明确定义变量是什么,明确组件需要开放的接口

  2. 我们需要理解每一个组件的核心功能是什么,可通过单一职责这样的设计模式来考虑。组件的核心功能是不能发生变化的,这也是为了接口的向后兼容

  3. 对于业务组件,我们可否直接把一些常年不发生变化的数据和UI绑定在一起,从而删减部分接口。大家不必担心耦合性,解耦的前提是有被解耦的需求。

四 开发组件

1 UI规范

UI规范是组件开发的物理依据,你得知道要做成什么样子,你才能做。UI规范要对整套组件的各个视觉元素(长,宽,padding,margin,圆角,颜色,字体,字号,边框,图标,阴影)有语意明确的定义和友好的标注,这样前端工程师们才能做到有法可依。

2 开发

举例:
1.sass或less来写样式
2.重置样式文件和样式的公共变量文件(将视觉元素翻译成代码中的常量形成的文件),这是根据自己的UI规范来决定的
3.ES6的哪些特性不使用,用哪一个stage的babel来翻译
4.需要引入的第三方包有哪些,这个也决定了最后打包出来组件js的大小
5.工程的目录结构

3 打包

对于Vue,我们通常使用的是webpack。具体配置这里不详细讲解,可以参考入门Webpack,看这篇就够了,还有一个快速方法就是通过Vue-cli生成的模板工程来进行更改。
接着我们要定义清楚我们的打包策略:

举例:
1.所有的样式打到一个文件
2.有fonts则单独打出来
3.组件JS和VueJS不打在一起
4.非通用的第三方包需和组件打在一起

五 最佳实践

以上四点是正式编写组件代码的前置工作。现在我们通过elementUI的源码来看一个最佳实践,我们的例子是比较简单的面包屑,先看一下怎么去使用的这个组件:

用法.png


然后我们来看一下代码如何实现的


<template>
  <span class="el-breadcrumb__item">
    <span class="el-breadcrumb__inner" ref="link" role="link">
      <slot></slot>
    </span>
    <i v-if="separatorClass" class="el-breadcrumb__separator" :class="separatorClass"></i>
    <span v-else class="el-breadcrumb__separator" role="presentation">{{separator}}</span>
  </span></template><script>
  export default {    name: 'ElBreadcrumbItem',    props: {      to: {},      replace: Boolean
    },
    data() {      return {        separator: '',        separatorClass: ''
      };
    },    inject: ['elBreadcrumb'],

    mounted() {      this.separator = this.elBreadcrumb.separator;      this.separatorClass = this.elBreadcrumb.separatorClass;      let self = this;      if (this.to) {        let link = this.$refs.link;
        link.setAttribute('role', 'link');
        link.addEventListener('click', _ => {          let to = this.to;
          self.replace ? self.$router.replace(to)
            : self.$router.push(to);
        });
      }
    }
  };</script>

props中就是ElBreadcrumbItem暴露出来的两个接口,也就是上文第三点提到的内容,接口的值是从父组件传过来的。我们再看一下ElBreadcrumb,也就是父组件的代码实现。

这里简单解释一下inject:inject和provide是成对出现的,是vue@2.2.0的新特性。通过此种方法变可以直接调用提供provide的组件中的属性了,总结就是依赖注入(DI)。

<template>
  <div class="el-breadcrumb" aria-label="Breadcrumb" role="navigation">
    <slot></slot>
  </div></template><script>
  export default {    name: 'ElBreadcrumb',    props: {      separator: {        type: String,        default: '/'
      },      separatorClass: {        type: String,        default: ''
      }
    },

    provide() {      return {        elBreadcrumb: this
      };
    },

    mounted() {      const items = this.$el.querySelectorAll('.el-breadcrumb__item');      if (items.length) {
        items[items.length - 1].setAttribute('aria-current', 'page');
      }
    }
  };</script>

slot其实是专门留给ElBreadcrumbItem的插槽,实际上ElBreadcrumb不涉及什么UI,它也通过props暴露出来了两个接口,这个两个的值是使用者传入的。我们可以看到,默认的分隔符是'/',如果你在ElBreadcrumbItem中通过属性传入的分隔符是'+',那面包屑每一级中的分隔符也会是'+',注意一下代码中的provide和上面的inject相对应。最后就是让组件可注册

import ElBreadcrumb from './src/breadcrumb';/* istanbul ignore next */ElBreadcrumb.install = function(Vue) {
  Vue.component(ElBreadcrumb.name, ElBreadcrumb);
};export default ElBreadcrumb;

通过给组件添加install方法,让组件可被Vue.use方法在全局注册。可参考Vue官方文档API

总结

本文简单梳理了一下组件开发的思路,重点在于组件开发的这些前置条件:

  • 定义组件

  • 划分颗粒度

  • 理清组件的核心接口

  • 如何定义打包策略和UI规范

完成这几点,从代码层面只是一小部分工作,但若把整个组件作为一个产品来看,就已经完成了一半了。

作者:PengL
链接:https://www.jianshu.com/p/dc83b40bc63c


0人推荐
随时随地看视频
慕课网APP