聊聊前端开发中Vue.js组件间的循环引用方法 - Web前端
作者:98wpeu发布时间:2026-09-11分类:网页前端技术浏览:5

聊聊前端开发中vue.js组件间的循环引用方法。
组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事:
什么是组件:
众所周知组件是Vue.js最强大的功能之一。组件可以扩展html元素,封装可重用的代码。在较高层面上,组件是自定义的元素,vue.JS的编译器为它添加特殊功能。在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展。下面话不多说了,来一起看看本文的正文内容。
引言
写了大大小小不少基于VUE的项目,但是基本没用到过组件循环引用的知识。
为了查缺补漏,照着官方文档撸一个DemO:https://cn.VueJs.org/v2/gUIde/components-Edge-cases.html
本人的运行版本为 Vue-cli@2.8.1,启用项目后,将以下 js 文件和 vue 文件放置在相应的目录中运行。
mAIn.js
import Vue from 'vue'
import APP from './app'
new Vue({
el: '#App',
template: '<App/>',
components: { App }
})main.js 导入 App 组件,并在 components 中注册 App 组件。
app.vue
<template>
<div id="app">
<li v-for="folder in folders">
<tree-folder v-bind:folder="folder"></tree-folder>
</li>
</div>
</template>
<script>
import TreeFolder from './components/tree-folder'
Export default {
data: function () {
return {
folders: [
{
name: 'folder1',
children: [{
name: 'folder1 - folder1',
children: [{
name: 'folder1 - folder1 - folder1'
}]
}, {
name: 'folder1 - folder2',
children: [{
name: 'folder1 - folder2 - folder1'
}, {
name: 'folder1 - folder2 - folder2'
}]
}]
},
{
name: 'folder 2',
children: [{
name: 'folder2 - folder1',
children: [{
name: 'folder2 - folder1 - folder1'
}]
}, {
name: 'folder2 - folder2',
children: [{
name: 'folder2-content1'
}]
}]
},
{
name: 'folder 3',
children: [{
name: 'folder3 - folder1',
children: [{
name: 'folder3 - folder1 - folder1'
}]
}, {
name: 'folder3 - folder2',
children: [{
name: 'folder3-content1'
}]
}]
}
]
}
},
components: {
TreeFolder
}
}
</script>App 组件导入 TreeFolder 组件,并在 components 中注册 TreeFolder 组件。
components/tree-folder.vue
<template>
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"></tree-folder-contents>
</p>
</template>
<script>
// 官方文档:「在我们的例子中,将 tree-folder 组件做为切入起点。我们知道制造矛盾的是 tree-folder-contents 子组件,所以我们在 tree-folder 组件的生命周期钩子函数 beforeCreate 中去注册 tree-folder-contents 组件」
export default {
props: ['folder'],
data: function () {
return {}
},
beforeCreate: function () {
// 官方文档给出的是require
// this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
// 在基于vue-cli@2.8.1按照上面的写法还是会报错
// Failed to mount component: template or render function not defined.
// 所以我们应该改为基于es6的写法异步加载一个组件如下
this.$options.components.TreeFolderContents = () => import('./tree-folder-contents.vue')
}
}
</script>TreeFolder 组件导入 TreeFolderContents 组件,并在 components 中注册 TreeFolderContents 组件。
components/tree-folder-contents.vue
<template>
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"></tree-folder>
<span v-else>{{ child.name }}</span>
</li>
</ul>
</template>
<script>
import TreeFolder from './tree-folder'
export default {
props: ['children'],
components: {
TreeFolder
}
}
</script>TreeFolderContents 组件又导入 TreeFolder 组件,并在 components 中注册 TreeFolder 组件,产生了循环引用。
组件之间的循环引用
假设你需要构建一个文件目录树,像访达或资源管理器那样的。你可能有一个 <tree-folder> 组件,模板是这样的:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>还有一个 <tree-folder-contents> 组件,模板是这样的:
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>当你仔细观察的时候,你会发现这些组件在渲染树中互为对方的后代和祖先——一个悖论!当通过 Vue.component 全局注册组件的时候,这个悖论会被自动解开。如果你是这样做的,那么你可以跳过这里。
然而,如果你使用一个模块系统依赖/导入组件,例如通过 webpack 或 Browserify,你会遇到一个错误:
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 时去注册它:
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}或者,在本地注册组件的时候,你可以使用 Webpack 的异步 import:
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
}这样问题就解决了!
相关推荐
- Python正则表达式匹配多行注释和文档字符串 - Web前端
- Windows Terminal:完整指南 - Web前端
- 微信小程序开发:随机生成验证码 - Web前端
- 了解下经过重构后的Microsoft Edge浏览器 - Web前端
- Adobe杀死Acrobat和Reader中的Flash –推出了这些重要的安全错误修复程序 - Web前端
- vue中setTimeout无法通过clearTimeout清除问题 - Web前端
- Windows 10 20H2更新:面向IT专业人员的新功能 - Web前端
- 在vscode里开发vue项目需要安装的插件:eslint - Web前端
- Twitter、YouTube、Facebook等知名网站已经开始强制限制IE了,您还在用IE? - Web前端
- 如何从DOM转换为SVG坐标并再次返回 - Web前端
- 网页前端技术排行
-
- 1[Web前端]用javascript实现默认图片替代未显示的图片 - Web前端
- 2分析Iconfont-阿里巴巴矢量常用图标库 - Web前端
- 3jQuery实例教程:制作网页中可折叠的面板 - Web前端
- 4【第六章】Foundation之按钮和下拉功能 - Web前端
- 5基于jquery的滚动条滚动固定div(附演示下载) - Web前端
- 6jQuery编写widget的一些技巧分享 - Web前端
- 7分享精心挑选的12款优秀jQuery Ajax分页插件和教程 - Web前端
- 8jQuery实现鼠标移到元素上动态提示消息框效果 - Web前端
- 9在Mac/PC上远程调试iPhone/iPad上的网页 - Web前端
- 最近发表
-
- WordPress随机显示特色图片插件:Random Post Thumbnails
- KeePass实现Chrome浏览器自动填充密码方法一
- LNMP一键包nginx 301强制跳转到https教程
- KeePass实现Chrome浏览器自动填充密码方法二
- #建站# 免费的VPS管理软件Xshell8/Xftp8中文版下载
- 使用Xshell 8连接VPS教程_电脑登录vps的方法
- WordPress评论界面添加烟花????效果
- 不同浏览器书签同步方案:坚果云+Floccus_详细使用教程
- iOS端KeePassXC客户端APP:Strongbox Password Safe
- 给WordPress评论中的Gravatar头像图片添加ALT属性


