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

slotBaseComponent 却无法正确渲染的原因,看下图:

这张图与上一张图相同,在子组件中打印 this.$vnode ,标注中的 context 引用着 VNode 被创建时所在的组件实例,由于 this.$vnode 中引用的 VNode 对象是在父组件中被创建的,所以 this.$vnode 中的 context 引用着父实例。理论上图中标注的两个 context 应该是相等的:

复制代码 代码如下:

console.log(this.$vnode.context === this.$vnode.componentOptions.children[0].context) // true

Vue 内部做了一件很重要的事儿,即上面那个表达式必须成立,才能够正确处理具名 slot ,否则即使 slot 具名也不会被考虑,而是被作为默认插槽。这就是高阶组件中不能正确渲染 slot 的原因。

那么为什么高阶组件中上面的表达式就不成立了呢?那是因为由于高阶组件的引入,在原本的父组件与子组件之间插入了一个组件( 也就是高阶组件 ),这导致在子组件中访问的 this.$vnode 已经不是原来的父组件中的 VNode 片段了,而是高阶组件的 VNode 片段,所以此时 this.$vnode.context 引用的是高阶组件,但是我们却将 slot 透传, slot 中的 VNodecontext 引用的还是原来的父组件实例,所以这就造成了以下表达式为假:

复制代码 代码如下:

console.log(this.$vnode.context === this.$vnode.componentOptions.children[0].context) // false

最终导致具名插槽被作为默认插槽,从而渲染不正确。

而解决办法也很简单,只需要手动设置一下 slotVNodecontext 值为高阶组件实例即可,修改高阶组件如下:

hoc.js

function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 props: WrappedComponent.props,
 render (h) {
  const slots = Object.keys(this.$slots)
  .reduce((arr, key) => arr.concat(this.$slots[key]), [])
  // 手动更正 context
  .map(vnode => {
   vnode.context = this._self
   return vnode
  })

  return h(WrappedComponent, {
  on: this.$listeners,
  props: this.$props,
  attrs: this.$attrs
  }, slots)
 }
 }
}

现在,都能够正常渲染啦,如下图:

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

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