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

  • 054_末晨曦Vue技术_处理边界情况之组件之间的循环引用已关闭评论
  • 26 次浏览
  • A+
所属分类:Web前端
摘要

点击打开视频讲解更详细假设你需要构建一个文件目录树,像访达或资源管理器那样的。你可能有一个 <tree-folder> 组件,模板是这样的:


组件之间的循环引用

点击打开视频讲解更详细

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

srccomponentsHelloWorld.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> 

srccomponentsCategory.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> 

srcmain.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站一键三连哦!感谢支持!