全部 / 前端 / 技术 · 2022年8月18日 0

Vue 组件循环引用

最近大屏组件内要新增一个组件:画布组件,可以把另一个大屏的组件渲染到其中。思路是,既然预览的时候可以在弹框内可以把大屏渲染出来,那就可以在指定的组件内渲染出来。

弹框预览代码:(AbsRun 是预览组件)

<dialog>
    <AbsRun :id='id'></AbsRun>
</dialog>

我设想的画布组件代码:

<div id='canvas-panel'>
    <AbsRun :id="id"></AbsRun>
</div>

运行后发现报错:

看上面的报错,梳理的好久才发现产生了组件循环引用:

首先画布组件需要使用 AbsRun 组件,所以代码如下:

// 画布组件

import AbsRun from '../abs-run.vue'

components:{
    AbsRun,
}

AbsRun 组件是预览的,所以又得需要注册所有的组件,当然包括画布组件:

import xxx from '../components/xxx.vue'
import CanvasPanel from '../components/canvas-panel.vue'

components:{
    xxx,
    CanvasPanel,
}

至此就产生了循环引用问题,那如何解决呢?

为了解释这里发生了什么,我们先把两个组件称为 A 和 B。模块系统发现它需要 A,但是首先 A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A 反正是需要 B 的,但是我们不需要先解析 B。”

在我们的例子中,把  <tree-folder> 组件设为了那个点。我们知道那个产生悖论的子组件是 <tree-folder-contents>  组件,所以我们会等到生命周期钩子 beforeCreate 时去注册它:

beforeCreate: function () {
  this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}

或:

components: {
  TreeFolderContents: () => import('./tree-folder-contents.vue')
}

那在我的这种场景下,画布组件就是产生悖论的子组件,所以等到生命周期钩子 beforeCreate 时再去注册 AbsRun:

beforeCreate(){
  this.$options.components.AbsRun = () => import('../abs-run')
},

参考:

  1. https://v2.cn.vuejs.org/v2/guide/components-edge-cases.html#%E7%BB%84%E4%BB%B6%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8