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, }) } } }
内容版权声明:除非注明,否则皆为本站原创文章。