Webpack 动态引入组件
// 假设文件夹路径是 @/components
const modules = {};
const files = require.context('@/components', false, /\.vue$/);
files.keys().forEach(key => {
const name = key.replace(/(\.\/|\.vue)/g, '');
modules[name] = files(key).default || files(key);
})
export default modules;
Vite 动态引入组件
// 获取所有 .vue 文件
const modules = import.meta.glob('@/components/*.vue');
// 如果需要立即加载所有模块
const modulesEager = import.meta.glob('@/components/*.vue', { eager: true });
// 转换为组件对象
const components = {};
for (const path in modulesEager) {
const name = path.split('/').pop().replace(/\.vue$/, '');
components[name] = modulesEager[path].default;
}
export default components;
使用场景
动态注册全局组件
主要在应用入口文件 (main.js) 中使用
使用常规的 app.component 注册
直接注册,不显式处理异步加载
更适合一次性全局注册
相对简单直接
// main.js 或单独的工具文件
import { createApp } from 'vue';
const app = createApp(App);
// Vite 方式
const modules = import.meta.glob('./components/*.vue');
Object.entries(modules).forEach(([path, module]) => {
const componentName = path
.split('/')
.pop()
.replace(/\.\w+$/, '');
module().then((mod) => {
app.component(componentName, mod.default);
})
});
app.mount('#app');
动态导入并自动注册
可以在任何地方使用,更具模块化
使用 defineAsyncComponent 进行异步组件定义
明确使用异步组件加载
更适合模块化、可复用的组件注册逻辑
更灵活,可以添加更多自定义逻辑
// utils/dynamicComponents.js
export function registerDynamicComponents(app) {
// 1. 使用 import.meta.glob 获取所有组件
const components = import.meta.glob('@/components/custom/**/*.vue')
// 2. 遍历并注册组件
for (const path in components) {
// 从路径提取组件名(可自定义命名规则)
const componentName = path
.split('/')
.pop()
.replace(/\.vue$/, '')
// 可选:将驼峰式转换为连字符式 MyComponent -> my-component
.replace(/([a-z])([A-Z])/g, '$1-$2')
.toLowerCase()
// 3. 使用 defineAsyncComponent 定义异步组件
app.component(
componentName,
defineAsyncComponent({
loader: components[path],
loadingComponent: LoadingSpinner, // 加载中的组件
delay: 200, // 延迟显示加载状态
errorComponent: ErrorComponent, // 错误时显示的组件
timeout: 3000 // 超时时间
})
)
}
}
// 在 main.js 中使用
import { createApp } from 'vue'
import App from './App.vue'
import { registerDynamicComponents } from './utils/dynamicComponents'
const app = createApp(App)
registerDynamicComponents(app)
app.mount('#app')
动态导入并注册指令
import type { App } from 'vue'
// 类型定义
type DirectiveModule = {
default: {
name?: string
[key: string]: any
}
}
type DirectiveLoader = () => Promise<DirectiveModule>
export async function registerDirectives(app: App) {
// 1. 动态导入所有指令文件
const directiveFiles = import.meta.glob<DirectiveLoader>('./*.ts', { import: 'default', eager: false })
// 2. 排除 index.ts 文件
const filteredFiles = Object.entries(directiveFiles).filter(([path]) => !path.includes('index.ts'))
// 3. 加载并注册指令
await Promise.all(
filteredFiles.map(async ([path, loader]) => {
try {
const module = await loader()
const directive = module
// 获取指令名 (从文件名或指令对象的 name 属性)
const directiveName = path.split('/').pop()?.replace(/\.ts$/, '')
if (directiveName) {
// 注册指令 (自动添加 v- 前缀)
app.directive(directiveName, directive)
console.log(`[Directive] Registered: v-${directiveName}`)
}
} catch (error) {
console.error(`[Directive] Failed to load ${path}:`, error)
}
})
)
}
微前端子应用 (独立组件注册)
// 子应用入口文件
export async function registerComponentsToHost(app) {
const componentModules = import.meta.glob('./components/*.vue')
const registrationPromises = Object.entries(componentModules).map(
async ([path, loader]) => {
const componentName = `SubApp${path.split('/').pop().replace(/\.vue$/, '')}`
const component = await loader()
return { name: componentName, component: component.default }
}
)
const components = await Promise.all(registrationPromises)
// 返回注册信息供主应用使用
return {
install: (hostApp) => {
components.forEach(({ name, component }) => {
hostApp.component(name, component)
})
}
}
}
// 主应用中使用
const subAppComponents = await import('sub-app/componentRegistry')
subAppComponents.install(mainApp)
延迟加载可视化编辑器组件
只在用户进入编辑器页面时才加载相关组件
// 编辑器组件懒加载
const EditorComponentLoader = {
install(app) {
// 注册一个虚拟组件作为占位
app.component('LazyEditor', {
template: '<div class="editor-placeholder"></div>',
async mounted() {
// 实际加载编辑器组件
const editors = import.meta.glob('../editors/*.vue')
const { default: TextEditor } = await editors['../editors/TextEditor.vue']()
const { default: ImageEditor } = await editors['../editors/ImageEditor.vue']()
// 动态替换当前组件
this.$options.components.TextEditor = TextEditor
this.$options.components.ImageEditor = ImageEditor
this.$forceUpdate()
}
})
}
}
// 在应用中使用
app.use(EditorComponentLoader)
动态绑定到特定组件
<template>
<div>
<!-- 动态渲染组件 -->
<component
v-for="(comp, name) in dynamicComponents"
:key="name"
:is="comp"
/>
</div>
</template>
<script setup>
import { ref, onMounted, defineAsyncComponent } from 'vue'
const dynamicComponents = ref({})
onMounted(async () => {
// 动态导入指定目录下的组件
const modules = import.meta.glob('@/components/custom/*.vue')
// 转换为组件对象
for (const path in modules) {
const name = path.split('/').pop().replace(/\.vue$/, '')
dynamicComponents.value[name] = defineAsyncComponent({
loader: modules[path],
loadingComponent: LoadingSpinner
})
}
})
</script>
按需加载特定组件
// 动态加载指定名称的组件
async function loadSpecificComponent(componentName) {
// 1. 构建组件路径模式
const componentPath = `../components/custom/${componentName}.vue`
// 2. 使用 import.meta.glob 的精确匹配
const componentModules = import.meta.glob('../components/custom/*.vue', {
import: 'default',
eager: false
})
// 3. 检查并加载指定组件
if (componentModules[componentPath]) {
try {
const module = await componentModules[componentPath]()
return module.default || module
} catch (error) {
console.error(`加载组件 ${componentName} 失败:`, error)
return null
}
}
return null
}
// 使用示例
const myComponent = await loadSpecificComponent('MySpecialComponent')
位运算实现权限控制
上一篇