优秀的编程知识分享平台

网站首页 > 技术文章 正文

054_末晨曦Vue技术_处理边界情况之组件之间的循环引用

nanyue 2024-08-08 18:39:42 技术文章 15 ℃

组件之间的循环引用

054_末晨曦Vue技术_处理边界情况之组件之间的循环引用_哔哩哔哩_bilibili

假设你需要构建一个文件目录树,像访达或资源管理器那样的。你可能有一个` <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')

}

案例:

<template>

<div id="app">

<li v-for="(folder,index) in folders" :key="index">

<HelloWorld :folder="folder"></HelloWorld>

</li>

</div>

</template>

<script>

import HelloWorld from './components/HelloWorld.vue'

export default {

name: 'App',

data(){

return {

folders: [

{

name: '末晨曦吖',

children: [{

name: '末晨曦吖 - 1',

children: [{

name: '末晨曦吖 - 1 - 1'

}]

}]

},

{

name: '满天星辰',

children: [{

name: '满天星辰 - 2',

children: [{

name: '满天星辰 - 2 - 2'

}]

}]

}

]

}

},

mounted() {

},

components:{

HelloWorld

},

methods:{

}

}

</script>

<style scoped>

</style>

`src\components\HelloWorld.vue`

<template>

<div class="hello">

<span>{{ folder.name }}</span>

<Category :children="folder.children"></Category>

</div>

</template>

<script>

import Category from './Category.vue'

export default {

name: 'HelloWorld',

props: ['folder'], //接收的是对象

data(){

return{

}

},

mounted(){

},

components:{

Category

},

methods:{

}

}

</script>

<style scoped>

</style>

`src\components\Category.vue`

<template>

<div id="app">

<ul>

<li v-for="(child,index) in children" :key="index">

<HelloWorld v-if="child.children" :folder="child"></HelloWorld>

<span v-else>{{ child.name }}</span>

</li>

</ul>

</div>

</template>

<script>

// import HelloWorld from './HelloWorld.vue'

export default {

name: 'Category',

props: ['children'], //接收的是数组

data(){

return {

name:'Category'

}

},

// 第二个解决组件之间的循环引用方式

// beforeCreate: function () {

// this.$options.components.HelloWorld = require('./HelloWorld.vue').default

// },

mounted() {

},

components:{

// HelloWorld

// 第三个解决组件之间的循环引用方式

HelloWorld: () => import('./HelloWorld.vue')

},

methods:{

}

}

</script>

<style scoped>

</style>

`src\main.js`

import Vue from 'vue'

import App from './App.vue'

//引入ElementUI组件库

import ElementUI from 'element-ui';

//引入ElementUI全部样式

import 'element-ui/lib/theme-chalk/index.css';

//插件引入

// import {Plugin1,Plugin2} from './plugins/plugins.js'

// 全局组件注册 // 第一个解决组件之间的循环引用方式

// import HelloWorld from './components/HelloWorld'

// import Category from './components/Category'

// Vue.component('HelloWorld',HelloWorld)

// Vue.component('Category',Category)

Vue.config.productionTip = false

//使用ElementUI

Vue.use(ElementUI)

// Vue.use(Plugin1,'参数1')

// Vue.use(Plugin2,'参数2')

new Vue({

render: h => h(App),

}).$mount('#app')

[若对您有帮助,请点击跳转到B站一键三连哦!感谢支持!](https://www.bilibili.com/video/BV17d4y1S7Cj?p=54&vd_source=66e2692cc471862d6c3f85dc4b9ea5dd)

最近发表
标签列表