054_末晨曦Vue技術_處理邊界情況之組件之間的循環引用

組件之間的循環引用

點擊打開影片講解更詳細

假設你需要構建一個文件目錄樹,像訪達或資源管理器那樣的。你可能有一個 <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站一鍵三連哦!感謝支援!