Vue的高阶组件,HOC使用举例

​Vue的高阶组件在官方文档中并未提及,这个是一个舶来品,是React生态才有的一个概念。

但不妨碍我们使用它。

实际上Vue组件就是一个对象。根据高阶函数的概念

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  1. 接受一个或多个函数作为输入。
  2. 输出一个函数

高阶组件也就是返回一个组件(对象数据)。

我们知道在使用Vue过程中,书写模板的方式有三种:

  1. 使用render
  2. 使用template属性
  3. 使用使用template模板

上述三种方式同时存在时,render的优先级是最高的。这在源码上是Vue首先对render这个参数做个判断,一旦有就直接使用了。

高阶组件有什么用呢?之前遇到一个问题,怎么对某些组件进行全局有效性拦截?我给出的方案是用mixins混入的方案,实际还可以用高阶组件。

比如我有一个Base.vue组件,一旦这个组件使用需要打印一些内容。

// Base.vue组件
<template>
  <div>
    <span @click="handleClick">props: {{test}}</span>
    <slot></slot>
  </div>
</template>
<script>

export default {
  name: 'Base',
  props: {
    test: Number
  },
  methods: {
    handleClick () {
      this.$emit('Base-click',{
        msg:'子组件emit'
      })
    }
  }
}
</script>

我们可以使用template构建一个高阶组件,然后包裹Base

//template版本
export default function HOC (Base) {
    return {
      template: '<base v-on="$listeners" v-bind="$attrs"/>',
      components: {
        base: Base
      },
      mounted () {
        console.log('我是HOC mounted log')
      }
    }
  }

但是这个方案要求使用的vue是完整版而不是运行时版本。

使用render构建的高阶组件

export default function Console (Base) {
    return {
      mounted () {
        console.log('我是HOC mounted log')
      },
      props: Base.props, // 继承pros
      render (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)
      }
    }
}

使用高阶组件

<template>
  <div>
    <wrapBase @Base-click="click1" :test="100">
       <p>default slot</p>
    </wrapBase>
  </div>
</template>
<script>
//Parent.vue
import Base from "./Base.vue";
// 使用hoc替代minxin
import HOC from "./hoc"; // 这里可以是template版本也可以是render版本
const wrapBase = HOC(Base);

export default {
  components: {
    Base,
    wrapBase,
  },
  methods: {
      click1(msg){
          console.log(msg, 'msg')
      }
  }
  //...
};
</script>

以上案例高阶组件用来进行日志输出。当然也可以用来做权限校验。

也可以参考文章:https://zhuanlan.zhihu.com/p/181673485