介绍
vue 动态组件的应用场景很多,可应用于动态页签,动态路由等场景,其核心原理是批量注册。在vue2和vue3中实现原理相同,只是语法略有差异。
vue2 实现
基于 webpack
require.context() 是webpack提供的一个自动导入的api 参数1:加载的文件目录 参数2:是否加载子目录 参数3:正则,匹配文件 返回值:导入函数 fn 使用require提供的函数context加载某一个目录下所有的.vue后缀的文件,他的返回值是一个对象,对象里面有一个属keys(), 可以获取所有的文件路径,我们可以遍历importfn.keys(),最后在遍历中使用
首先先描述下应用场景,比如我想在父容器遍历组件集合,根据组件类型(字符串)来首先动态加载组件,如下图:
如果要是按正常一个个注册的话,也是可以的,就是代码冗余多,不够优雅,所以需要一个方法来实现,这个方法就放到一个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;
然后在页面引用,如下图:
好了,到这里简简单单就实现了,我现在整个项目需要这样批量注册的场景也就两三个,所以我在需要批量注册的组件对应的文件夹就放置这个一个index.js就能实现了,如果实际场景不想放那么多,可以自行稍微改造下,传个路径进去,我vue3版本就是这样实现的
vue3 实现
基于 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;
这个并不完美,理想中应该是根据传参(需要动态注册的组件所在文件夹路径)来实现,但是,如下目前好像并不支持:
// 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 使用了变量,所以会报错.
这个有其他解决办法,待会下面会说到,因为我在该项目批量注册应用场景不多,所以我就直接传参判断来写死了.
.接下来就是引用了,如下图:
至此,就实现批量动态注册了,另外注意, 组件集合不要用绑定模式,虽然不报错,但是会报黄提示影响效率
// let components =ref({}) //不要写成响应式的了,会有性能风险提示 let components = {} components = loadcomponentsfromfolder('widget')
最后
用其他办法来解决这个问题吧,有点复杂,有更好办法的小伙伴可以留言~
使用 fs 模块读取文件列表: 在 node.js 环境中使用 fs 模块读取指定文件夹下的文件列表。 将文件列表传递给前端,前端再使用 import() 动态导入这些文件。 前端动态导入: 前端根据接收到的文件列表动态导入组件。 实现步骤 1. 后端读取文件列表 首先,在 vite 项目的 vite.config.js 或者单独的 node.js 脚本中,使用 fs 模块读取文件列表,并将结果暴露给前端。 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. 前端动态导入 在前端,使用 import() 动态导入这些文件。 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 = {}; // 动态导入模块 await promise.all(componentpaths.map(async (path) => { const componentname = path.match(/\/([^/]+)\.vue$/)[1]; components[componentname] = defineasynccomponent(() => import(path)); })); // 缓存结果 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. 创建 api 端点 在 vite 项目中创建一个简单的 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 函数时,只需传递不同的 folderpath 参数即可动态注册不同路径下的组件。 javascript // 在某个 vue 组件中使用 <script setup> import { ref, onmounted } from 'vue'; import loadcomponentsfromfolder from '@/utils/dynamiccomponents'; const folderpath = 'widgetattr'; // 可以根据实际需求动态设置 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 系统中,路径分隔符需要转换。 api 端点: 确保 api 端点能够正确返回文件路径列表。 性能优化: 如果组件数量较多,可以考虑使用懒加载和缓存机制来优化性能。 通过这种方式,你可以根据路径参数动态注册不同文件夹下的组件,而不需要使用 if-else 判断。
以上就是vue批量注册组件实现动态组件技巧的详细内容,更多关于vue动态组件的资料请关注代码网其它相关文章!
发表评论