对于习惯使用 Vue 框架的人来说,高阶组件(HOC)这个概念是相对比较陌生的;高阶组件在 React 框架中是比较常见的一个概念,但是 Vue 中也是可以使用到高阶组件的;
一、高阶组件
1、什么是高阶函数?
高阶函数是为一个函数接受一个函数作为参数并返回一个新函数,使得新函数具有这个函数原先的功能又能够自定义的添加新的功能;
2、 什么是高阶组件?
高阶组件是一个将组件作为参数并返回一个新组件的函数‘;从这里来看,高阶组件其实是一个函数;因此高阶组件也具有高阶函数的一些特性;返回的新组件具有原组件的功能,同时也有了新自定义的功能;
具体点高阶组件其实就是装饰者模式的应用: 在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为;
3、特性
1、高阶组件(HOC)应该是无副作用的纯函数,且不应该修改原组件,即原组件不能有变动
2、高阶组件(HOC)不关心你传递的数据(props)是什么,并且新生成组件不关心数据来源
3、高阶组件(HOC)接收到的 props 应该透传给被包装组件即直接将原组件prop传给包装组件
4、高阶组件完全可以添加、删除、修改 props
4、使用
1、原始组件
// original.vue组件
<template>
<div>
<span @click="handleClick">props: {{test}}</span>
<slot></slot>
</div>
</template>
<script>
export default {
name: 'original',
props: {
test: Number
},
methods: {
handleClick () {
this.$emit('original-click',{
msg:'子组件emit'
})
}
}
}
</script>
2、使用 render 构建的高阶组件
export default function newCom(base) {
return {
mounted () {
console.log('我是HOC mounted log')
},
props: base.props, // 继承pros
render (h) {
// 将 this.$slots 格式化为数组,因为 h 函数第三个参数是子节点,是一个数组
const slots = Object.keys(this.$slots)
.reduce((acc, cur) => acc.concat(this.$slots[cur]), [])
// 手动更正 context
.map(vnode => {
vnode.context = this._self //绑定到高阶组件上
return vnode
}) // 继承slots
return h(base, {
on: this.$listeners,
props: this.$props,
attrs: this.$attrs
}, slots)
}
}
}
3、使用
import original from "@/components/original.vue";
import newComfrom "@/hoc/newCom.js";
const wrapOriginal = newCom(original);
Vue.component("wrap-original ", wrapOriginal );
上面的 newCom 就是一个高阶组件,他接收一个组件作为参数,又通过 render 函数返回一个新的组件;newCom 在返回新组件之前我们可以对原始组件 test 进行改造和继承;
二、高阶组件和 mixins 区别
高阶组件和 mixins 其实是互通的,他们俩是可以互相替代的,但是由于他们各自的特性影响,所以在 vue 框架大家更倾向于去使用 mixins,下面就看看他们俩之间的区别:
1、重用性:因为 minxins 对原组件具有侵入性,这会导致原来组件的可重用性降低,而高阶组件不会,高阶组件对原组件只是一个调用关系,并没有修改原来组件任何内容。
2、可测试性:因为高阶组件只是一个嵌套关系,在组件测试的时候,可以单独的测试原始组件和高阶组件;
3、层级问题:如果高阶组件嵌套层级太深,会导致出错的时候调试困难的问题;同时嵌套太多,因为有更多的组件实例,和组件属性传递增加,会带来更多性能上的消耗,以及传值问题;
4、稳定性:高阶组件会保持原有组件不被影响,不管外面如果改变都不会影响内部的组件;