vue高阶组件

274 阅读3分钟

本文主要讲vue高阶组件的实践

需求

把大象放进冰箱需要几个步骤:

  1. 把冰箱门打开
  2. 把大象放进去
  3. 把冰箱门关上

分析

组件结构

上面的几个按钮就是tab,可看作一个组件,下面的步骤内容就是不同的步骤组件。
一个tab对应一个步骤组件。
现在的思路是想写一个高阶组件,把tab名字和对应的步骤组件作为一个参数,传进一个函数中,由这个函数再包装成一个新组件,以实现这样可点击的tab,类似于这样

// firstContent是一个组件   
function hoc(modules = [{key: 'first', name: '第一步', component: firstContent},...]){
    return newComponent
}

这样就可以实现复用,比如说现在把长颈鹿放进冰箱需要几步?

高阶组件

高阶组件,顾名思义就是写一个函数,传入一个组件,进行包装,返回一个新的组件。具体是利用高阶函数的思想。
首先要知道vue组件,其实就是一个对象。一般写vue组件会在.vue文件中写template,script, style。在script写js的时候会写到export default {...},其实就是.vue文件export一个对象,和在.js文件中export一个对象一样的道理。在使用这个组件的时候就会import这个对象
vue在进行局部组件注册的时候可以传入一个对象,比如这样

var Child = {
  template: '<div>A custom component!</div>',
  data() {
      return {}
  },
  created(){}
}

new Vue({
  // ...
  components: {
    'my-component': Child
  }
})

实现

先介绍一下文件目录结构

目录结构
首先开发一个tab的组件,名字为tabGroup,props为父组件传过来的tabs,包含了key、name, 也就是[{key: 'first', name: '第一步'}, ...],每个按钮有一个点击事件,同时在监听函数中,调用父组件的select方法

// tabGroup.vue
<template>
  <div class="wrapper">
    <h3>VUE HOC</h3>
    <ul class="tab-group">
      <li
        v-for="(item, index) in tabs"
        :key="index"
        class="tab-item"
      >
      <el-button v-if="active == item.key" type="primary" @click.stop="select(item.key)">{{ item.name }}</el-button>
      <el-button v-else @click.stop="select(item.key)">{{ item.name }}</el-button>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: "tabGroup",
  props: {
    tabs: {
      type: Array,
      default: function() {
        return [];
      }
    }
  },
  data() {
    return {
      active: this.tabs[0].key
    };
  },
  methods: {
    select(tab) {
      this.active = tab;
      this.$parent.select(tab);
    }
  }
};
</script>

重点来了,现在写一个HOC,也就是一个函数,参数为modules,数据结构是这样[{ key: "first", name: "第一步", component: firstContent },...],firstContent为一个组件对象
这个函数会返回一个组件,其实就是对象。这个组件注册了tabGroup组件,步骤组件,同时利用动态组件的:is属性,来决定显示哪个步骤组件

// hoc.js
import tabGroup from "./tabGroup.vue";
export default function tabHoc(modules) {
  let components = {};
  let tabs = [];
  function createContent(modules) {
    modules.forEach(value => {
      components[value.key] = value.component;
      tabs.push({ key: value.key, name: value.name });
    });
  }
  createContent(modules);
  return {
    template:
      '<div><tab-group :tabs="tabs"/><div style="margin: 15px" :is="activePage"></div></div>',
    components: {
      tabGroup,
      ...components
    },
    data() {
      return {
        tabs,
        activePage: tabs[0].key
      };
    },
    methods: {
      select(moduleName){
        this.activePage = moduleName;
      }
    }
  };
}

接下来就是步骤组件

// first.vue
<template>
  <div class="wrapper">把冰箱门打开</div>
</template>
<script>
export default {
  components: {},
  data() {
    return {};
  }
};
</script>
// second.vue
<template>
  <div class="wrapper">把大象放进去</div>
</template>
<script>
...
</script>

// third.vue
<template>
  <div class="wrapper">把冰箱门关上</div>
</template>
<script>
...
</script>

最后就是把上面所有的组件,函数组合在一个,实现一个新的大组件。分别引入hoc, 和不同步骤组件,把这些步骤组件作为参数传入hoc函数中,返回了一个对象,赋值给tabContainer,作为新组件。
这里的tabHoc其实和import作用一样,import Acomponent from './Acomponent.vue' 和const Acomponent = tabHoc()是相同的作用,因为都是返回一个组件对象

// HellWorld.vue
<template>
  <div class="wrapper">
    <tab-container></tab-container>
  </div>
</template>
<script>
import tabHoc from "./tabGroup/hoc.js";
import firstContent from "./first.vue";
import secondContent from "./second.vue";
import thirdContent from './third.vue';
export default {
  name: "HelloWorld",
  data() {
    return {};
  },
  components: {
    tabContainer: tabHoc([
      { key: "first", name: "第一个", component: firstContent },
      { key: "second", name: "第二个", component: secondContent },
      { key: "third", name: "第三步", component: thirdContent }
    ])
  }
};
</script>

到现在就实现完毕了。
下一篇文章介绍如何用插槽解决同样的问题