組件之間的迴圈引用 點擊打開視頻講解更詳細 假設你需要構建一個文件目錄樹,像訪達或資源管理器那樣的。你可能有一個 <tree-folder> 組件,模板是這樣的: <p> <span>{{ folder.name }}</span> <tree-folder-contents :children=" ...
組件之間的迴圈引用
假設你需要構建一個文件目錄樹,像訪達或資源管理器那樣的。你可能有一個 <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')