探索Vue高阶组件的使用(4)

import BaseComponent from './base-component.vue'
console.log(BaseComponent)

思考一下,这里的 BaseComponent 是什么?它是函数吗?不是,虽然单文件组件会被 vue-loader 处理,但处理后的结果,也就是我们这里的 BaseComponent 仍然还是一个普通的 JSON 对象,只不过当你把这个对象注册为组件( components 选项)之后, Vue 最终会以该对象为参数创建一个构造函数,该构造函数就是生产组件实例的构造函数,所以在 Vue 中组件确实是函数,只不过那是最终结果罢了,在这之前我们完全可以说在 Vue 中组件也可以是一个普通对象,就像单文件组件中所导出的对象一样。

基于此,我们知道在 Vue 中一个组件可以以纯对象的形式存在,所以 Vue 中的高阶组件可以这样定义: 接收一个纯对象,并返回一个新的纯对象 ,如下代码:

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 template: '<wrapped v-on="$listeners" v-bind="$attrs"/>',
 components: {
  wrapped: WrappedComponent
 },
 mounted () {
  console.log('I have already mounted')
 }
 }
}

WithConsole 就是一个高阶组件,它接收一个组件作为参数: WrappedComponent ,并返回一个新的组件。在新的组件定义中,我们将 WrappedComponent 注册为 wrapped 组件,并在 template 中将其渲染出来,同时添加 mounted 钩子,打印 I have already mounted

以上就完成了与 mixins 同样的功能,不过这一次我们采用的是高阶组件,所以是非侵入式的,我们没有修改原组件( WrappedComponent ),而是在新组件中渲染了原组件,并且没有对原组件做任何修改。并且这里大家要注意 $listeners$attrs

'<wrapped v-on="$listeners" v-bind="$attrs"/>'

这么做是必须的,这就等价于在 React 中透传 props

<WrappedComponent {...this.props}/>

否则在使用高阶组件的时候,被包装组件( WrappedComponent )接收不到 props事件

那这样真的就完美解决问题了吗?不是的,首先 template 选项只有在完整版的 Vue 中可以使用,在运行时版本中是不能使用的,所以最起码我们应该使用渲染函数( render )替代模板( template ),如下:

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 render (h) {
  return h(WrappedComponent, {
  on: this.$listeners,
  attrs: this.$attrs,
  })
 }
 }
}
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/467.html