Vue批量注冊組件實現(xiàn)動態(tài)組件技巧
介紹
Vue 動態(tài)組件的應用場景很多,可應用于動態(tài)頁簽,動態(tài)路由等場景,其核心原理是批量注冊。在Vue2和Vue3中實現(xiàn)原理相同,只是語法略有差異。
Vue2 實現(xiàn)
基于 webpack
require.context() 是webpack提供的一個自動導入的API 參數(shù)1:加載的文件目錄 參數(shù)2:是否加載子目錄 參數(shù)3:正則,匹配文件 返回值:導入函數(shù) fn 使用require提供的函數(shù)context加載某一個目錄下所有的.vue后綴的文件,他的返回值是一個對象,對象里面有一個屬keys(), 可以獲取所有的文件路徑,我們可以遍歷importFn.keys(),最后在遍歷中使用
首先先描述下應用場景,比如我想在父容器遍歷組件集合,根據(jù)組件類型(字符串)來首先動態(tài)加載組件,如下圖:

如果要是按正常一個個注冊的話,也是可以的,就是代碼冗余多,不夠優(yōu)雅,所以需要一個方法來實現(xiàn),這個方法就放到一個JS里去,然后把該index.js直接丟到widget-attr文件夾里.
//index.js
const requireComponent = require.context('./', false, /\w+\.vue$/)
let comps = {}
requireComponent.keys().map(fileName => {
let comp = requireComponent(fileName).default;
comps[comp.name] = comp
})
export default comps;
然后在頁面引用,如下圖:

好了,到這里簡簡單單就實現(xiàn)了,我現(xiàn)在整個項目需要這樣批量注冊的場景也就兩三個,所以我在需要批量注冊的組件對應的文件夾就放置這個一個index.js就能實現(xiàn)了,如果實際場景不想放那么多,可以自行稍微改造下,傳個路徑進去,我Vue3版本就是這樣實現(xiàn)的
Vue3 實現(xiàn)
基于 Vite
const components = import.meta.glob("./*.vue");
//注意 : import.meta.glob 不支持變量,
Vue3 使用 組合式 ,方法里也用到了Vite的語法,首頁也要把核心代碼封裝到 dynamicComponents.js里面
詳細如下
// utils/dynamicComponents.js
import { defineAsyncComponent } from 'vue';
function loadComponentsFromFolder(folderPath) {
const components = {};
let modules = import.meta.glob('@/components/form-designer/widget/*.vue', { eager: false });
if (folderPath == 'widgetArrt') {
modules = import.meta.glob('@/components/form-designer/widget/widget-attr/*.vue', { eager: false });
}
for (const path in modules) {
const componentName = path.match(/\/([^/]+)\.vue$/)[1];
components[componentName] = defineAsyncComponent(modules[path]);
}
return components;
}
export default loadComponentsFromFolder;
這個并不完美,理想中應該是根據(jù)傳參(需要動態(tài)注冊的組件所在文件夾路徑)來實現(xiàn),但是,如下目前好像并不支持:
// utils/dynamicComponents.js
function loadComponentsFromFolder(folderPath) {
//省略...
modules = import.meta.glob(`${folderPath}/*.vue`, { eager: false });
//省略...
}
export default loadComponentsFromFolder;
這樣寫會報錯,提示import.meta.glob不支持變量.
[plugin:vite:import-glob] Invalid glob import syntax: Expected glob to be a string, but got dynamic template literal //大致意思: 只能使用文本,而我們的 path 使用了變量,所以會報錯.
這個有其他解決辦法,待會下面會說到,因為我在該項目批量注冊應用場景不多,所以我就直接傳參判斷來寫死了.
.接下來就是引用了,如下圖:

至此,就實現(xiàn)批量動態(tài)注冊了,另外注意, 組件集合不要用綁定模式,雖然不報錯,但是會報黃提示影響效率
// let components =ref({}) //不要寫成響應式的了,會有性能風險提示
let components = {}
components = loadComponentsFromFolder('widget')
最后
用其他辦法來解決這個問題吧,有點復雜,有更好辦法的小伙伴可以留言~
使用 fs 模塊讀取文件列表:
在 Node.js 環(huán)境中使用 fs 模塊讀取指定文件夾下的文件列表。
將文件列表傳遞給前端,前端再使用 import() 動態(tài)導入這些文件。
前端動態(tài)導入:
前端根據(jù)接收到的文件列表動態(tài)導入組件。
實現(xiàn)步驟
1. 后端讀取文件列表
首先,在 Vite 項目的 vite.config.js 或者單獨的 Node.js 腳本中,使用 fs 模塊讀取文件列表,并將結(jié)果暴露給前端。
javascript
// vite.config.js 或者單獨的 Node.js 腳本
const fs = require('fs');
const path = require('path');
function getComponentPaths(folderPath) {
const baseFolderPath = path.resolve(__dirname, 'src/components/form-designer/widget/');
const fullFolderPath = folderPath ? path.join(baseFolderPath, folderPath) : baseFolderPath;
const files = fs.readdirSync(fullFolderPath);
const componentPaths = files
.filter(file => file.endsWith('.vue'))
.map(file => path.join(fullFolderPath, file));
return componentPaths.map(p => p.replace(/\\/g, '/').replace(path.resolve(__dirname, 'src/'), '@/'));
}
module.exports = {
getComponentPaths,
};
2. 前端動態(tài)導入
在前端,使用 import() 動態(tài)導入這些文件。
javascript
// utils/dynamicComponents.js
import { defineAsyncComponent } from 'vue';
const componentCache = {};
async function loadComponentsFromFolder(folderPath) {
// 檢查緩存
if (componentCache[folderPath]) {
return componentCache[folderPath];
}
// 獲取文件路徑列表
const componentPaths = await fetchComponentPaths(folderPath);
const components = {};
// 動態(tài)導入模塊
await Promise.all(componentPaths.map(async (path) => {
const componentName = path.match(/\/([^/]+)\.vue$/)[1];
components[componentName] = defineAsyncComponent(() => import(path));
}));
// 緩存結(jié)果
componentCache[folderPath] = components;
return components;
}
async function fetchComponentPaths(folderPath) {
// 這里假設你有一個 API 端點來獲取文件路徑列表
const response = await fetch(`/api/get-component-paths?folderPath=${folderPath}`);
const data = await response.json();
return data.paths;
}
export default loadComponentsFromFolder;
3. 創(chuàng)建 API 端點
在 Vite 項目中創(chuàng)建一個簡單的 API 端點來返回文件路徑列表。
javascript
// server/index.js
const express = require('express');
const { getComponentPaths } = require('../vite.config');
const app = express();
const port = 3000;
app.get('/api/get-component-paths', (req, res) => {
const folderPath = req.query.folderPath;
const paths = getComponentPaths(folderPath);
res.json({ paths });
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
使用示例
在組件或頁面中使用 loadComponentsFromFolder 函數(shù)時,只需傳遞不同的 folderPath 參數(shù)即可動態(tài)注冊不同路徑下的組件。
javascript
// 在某個 Vue 組件中使用
<script setup>
import { ref, onMounted } from 'vue';
import loadComponentsFromFolder from '@/utils/dynamicComponents';
const folderPath = 'widgetAttr'; // 可以根據(jù)實際需求動態(tài)設置
const dynamicComponents = ref({});
onMounted(async () => {
dynamicComponents.value = await loadComponentsFromFolder(folderPath);
});
</script>
<template>
<div>
<component v-for="(component, name) in dynamicComponents" :is="component" :key="name"></component>
</div>
</template>
注意事項
文件路徑處理:
確保文件路徑在前后端一致,特別是在 Windows 系統(tǒng)中,路徑分隔符需要轉(zhuǎn)換。
API 端點:
確保 API 端點能夠正確返回文件路徑列表。
性能優(yōu)化:
如果組件數(shù)量較多,可以考慮使用懶加載和緩存機制來優(yōu)化性能。
通過這種方式,你可以根據(jù)路徑參數(shù)動態(tài)注冊不同文件夾下的組件,而不需要使用 if-else 判斷。
以上就是Vue批量注冊組件實現(xiàn)動態(tài)組件技巧的詳細內(nèi)容,更多關于Vue動態(tài)組件的資料請關注腳本之家其它相關文章!
相關文章
vue3 Element-Plus el-backtop無效問題及解決
這篇文章主要介紹了vue3 Element-Plus el-backtop無效問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
Element InfiniteScroll無限滾動的具體使用方法
這篇文章主要介紹了Element InfiniteScroll無限滾動的具體使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07
詳解vue中使用express+fetch獲取本地json文件
本篇文章主要介紹了詳解vue中使用express+fetch獲取本地json文件,非常具有實用價值,需要的朋友可以參考下2017-10-10

