Vue批量注冊組件實現(xiàn)動態(tài)組件技巧
介紹
Vue 動態(tài)組件的應(yīng)用場景很多,可應(yīng)用于動態(tài)頁簽,動態(tài)路由等場景,其核心原理是批量注冊。在Vue2和Vue3中實現(xiàn)原理相同,只是語法略有差異。
Vue2 實現(xiàn)
基于 webpack
require.context() 是webpack提供的一個自動導(dǎo)入的API 參數(shù)1:加載的文件目錄 參數(shù)2:是否加載子目錄 參數(shù)3:正則,匹配文件 返回值:導(dǎo)入函數(shù) fn 使用require提供的函數(shù)context加載某一個目錄下所有的.vue后綴的文件,他的返回值是一個對象,對象里面有一個屬keys(), 可以獲取所有的文件路徑,我們可以遍歷importFn.keys(),最后在遍歷中使用
首先先描述下應(yīng)用場景,比如我想在父容器遍歷組件集合,根據(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)在整個項目需要這樣批量注冊的場景也就兩三個,所以我在需要批量注冊的組件對應(yīng)的文件夾就放置這個一個index.js就能實現(xiàn)了,如果實際場景不想放那么多,可以自行稍微改造下,傳個路徑進(jìn)去,我Vue3版本就是這樣實現(xiàn)的
Vue3 實現(xiàn)
基于 Vite
const components = import.meta.glob("./*.vue"); //注意 : import.meta.glob 不支持變量,
Vue3 使用 組合式 ,方法里也用到了Vite的語法,首頁也要把核心代碼封裝到 dynamicComponents.js里面
詳細(xì)如下
// 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;
這個并不完美,理想中應(yīng)該是根據(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 使用了變量,所以會報錯.
這個有其他解決辦法,待會下面會說到,因為我在該項目批量注冊應(yīng)用場景不多,所以我就直接傳參判斷來寫死了.
.接下來就是引用了,如下圖:
至此,就實現(xiàn)批量動態(tài)注冊了,另外注意, 組件集合不要用綁定模式,雖然不報錯,但是會報黃提示影響效率
// let components =ref({}) //不要寫成響應(yīng)式的了,會有性能風(fēng)險提示 let components = {} components = loadComponentsFromFolder('widget')
最后
用其他辦法來解決這個問題吧,有點復(fù)雜,有更好辦法的小伙伴可以留言~
使用 fs 模塊讀取文件列表: 在 Node.js 環(huán)境中使用 fs 模塊讀取指定文件夾下的文件列表。 將文件列表傳遞給前端,前端再使用 import() 動態(tài)導(dǎo)入這些文件。 前端動態(tài)導(dǎo)入: 前端根據(jù)接收到的文件列表動態(tài)導(dǎo)入組件。 實現(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)導(dǎo)入 在前端,使用 import() 動態(tài)導(dǎo)入這些文件。 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)導(dǎo)入模塊 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) { // 這里假設(shè)你有一個 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)設(shè)置 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ù)量較多,可以考慮使用懶加載和緩存機(jī)制來優(yōu)化性能。 通過這種方式,你可以根據(jù)路徑參數(shù)動態(tài)注冊不同文件夾下的組件,而不需要使用 if-else 判斷。
以上就是Vue批量注冊組件實現(xiàn)動態(tài)組件技巧的詳細(xì)內(nèi)容,更多關(guān)于Vue動態(tài)組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue select選擇框數(shù)據(jù)變化監(jiān)聽方法
今天小編就為大家分享一篇vue select選擇框數(shù)據(jù)變化監(jiān)聽方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08