稍微用过 Vue 单文件组件的朋友,都知道如何在一个组件中使用另一个组件:
<template>
<some-random-thing />
</template>
<script>
import SomeRandomThing from './components/SomeRandomThing'
export default {
components: {
SomeRandomThing,
},
}
</script>
很常见,很普通,可能会变得乏味...
我们来看一个需求,假设有一个 Header 组件,包含应用的布局和一些信息,这些信息可能与用户相关 - UserInfo 组件,也可能与公司相关 - CompanyInfo 组件,希望根据配置值显示其中一个。
- 方法一(可能是不少人的默认做法)
<template>
<div>
<company-info v-if="isCompany" />
<user-info v-else />
...
</div>
</template>
<script>
import UserInfo from './components/UserInfo'
import CompanyInfo from './components/CompanyInfo'
export default {
components: {
UserInfo,
CompanyInfo,
},
props: {
isCompany: { type: Boolean, default: false },
},
}
</script>
- 方法二:<component />
<template>
<div>
<component :is="componentName" />
</div>
</template>
<script>
import UserInfo from './components/UserInfo'
import CompanyInfo from './components/CompanyInfo'
export default {
components: {
UserInfo,
CompanyInfo,
},
props: {
isCompany: { type: Boolean, default: false },
},
computed: {
componentName () {
return this.isCompany ? 'company-info' : 'user-info'
},
},
}
</script>
似乎改善了一点,但是,还是需要 import 组件并通过 components 注册,不够精简。
- 方法三:动态引入 + <component />(有利于code splitting)
<template>
<div>
<component :is="componentInstance" />
</div>
</template>
<script>
export default {
props: {
isCompany: { type: Boolean, default: false },
},
computed: {
componentInstance () {
const name = this.isCompany ? 'CompanyInfo' : 'UserInfo'
return () => import(`./components/${name}`)
}
}
}
</script>
注意,this.isCompany 要在 import 语句外面访问,否则 isCompany 改变时不能触发 componentInstance 更新。
使用动态引入,Webpack 会在构建时为表达式中匹配的每个文件创建一个 chunk 文件,所以要确保在表达式中使用严格的 filter,使用名称和文件夹约定或者 webpack magic comments。