本文主要讲vue高阶组件的实践
需求
把大象放进冰箱需要几个步骤:
- 把冰箱门打开
- 把大象放进去
- 把冰箱门关上
分析
组件结构
上面的几个按钮就是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
}
})
实现
先介绍一下文件目录结构
// 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>
到现在就实现完毕了。
下一篇文章介绍如何用插槽解决同样的问题