• 循环引用
    • 递归组件
    • 组件之间的循环引用

    循环引用

    递归组件

    组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事:

    1. name: 'unique-name-of-my-component'

    当你使用 Vue.component 全局注册一个组件时,这个全局的 ID 会自动设置为该组件的 name 选项。

    1. Vue.component('unique-name-of-my-component', {
    2. // ...
    3. })

    稍有不慎,递归组件就可能导致无限循环:

    1. name: 'stack-overflow',
    2. template: '<div><stack-overflow></stack-overflow></div>'

    类似上述的组件将会导致“max stack size exceeded”错误,所以请确保递归调用是条件性的 (例如使用一个最终会得到 falsev-if)。

    组件之间的循环引用

    假设你需要构建一个文件目录树,像访达或资源管理器那样的。你可能有一个 <tree-folder> 组件,模板是这样的:

    1. <p>
    2. <span>{{ folder.name }}</span>
    3. <tree-folder-contents :children="folder.children"/>
    4. </p>

    还有一个 <tree-folder-contents> 组件,模板是这样的:

    1. <ul>
    2. <li v-for="child in children">
    3. <tree-folder v-if="child.children" :folder="child"/>
    4. <span v-else>{{ child.name }}</span>
    5. </li>
    6. </ul>

    当你仔细观察的时候,你会发现这些组件在渲染树中互为对方的后代祖先——一个悖论!当通过 Vue.component 全局注册组件的时候,这个悖论会被自动解开。如果你是这样做的,那么你可以跳过这里。

    然而,如果你使用一个模块系统依赖/导入组件,例如通过 webpack 或 Browserify,你会遇到一个错误:

    1. Failed to mount component: template or render function not defined.

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

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

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

    或者,在本地注册组件的时候,你可以使用 webpack 的异步 import

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

    这样问题就解决了!