基于Vite的Vue组件运行时如何导入调用外部.mjs文件?
我有一个基于Vite的Vue应用,希望动态导入外部文件。这些文件包含可插拔组件,应用在构建时无法知晓它们的存在。应用会从外部API获取配置,其中包含一个sources数组(我可以决定这些源是路径还是URL),每个源指向一个存放在.mjs文件中的组件。
部署时,需要能够将.mjs文件放置在dist目录的某个位置,应用可根据配置的路径尝试导入它。
我已成功从.vue文件旁的.mjs文件导入组件,但尝试从dist目录导入时遇到问题:
- 在
./dist目录下创建了foo.mjs:
const foo = function() { console.log("Success from Foo"); } export { foo }
- 在
./dist/assets目录下创建了bar.mjs:
const bar = function() { console.log("Success from Bar"); } export { bar }
- Vue组件中的调用代码:
<script setup lang="ts"> import { onMounted} from "vue"; onMounted(async () => { console.log("trying to call function from external file"); try { const { foo } = await import("/foo.mjs"); foo(); } catch { console.log("foo failed"); } try { const { bar } = await import("/assets/bar.mjs"); bar(); } catch { console.log("bar failed"); } }); </script>
运行后报错:
Failed to resolve import "/foo.mjs" from "src/App.vue". Does the file exist?
请问是否可以修改代码解决该问题,还是必须通过HTTP从外部文件服务器请求这些文件?
不需要依赖外部HTTP服务器,通过调整导入方式和文件存放位置即可解决:
1. 正确存放外部文件
将你的.mjs文件放到项目根目录的public文件夹下(没有则创建):
foo.mjs直接放在public/下bar.mjs放在public/assets/下
Vite会自动将public目录下的文件原样复制到构建后的dist目录,且开发/生产环境都能直接通过根路径访问这些文件。
2. 修改动态导入逻辑
使用new URL()结合import.meta.url构建运行时URL,避免Vite在构建阶段解析静态路径:
<script setup lang="ts"> import { onMounted} from "vue"; onMounted(async () => { console.log("trying to call function from external file"); try { const { foo } = await import(new URL('/foo.mjs', import.meta.url)); foo(); } catch (e) { console.error("foo failed:", e); } try { const { bar } = await import(new URL('/assets/bar.mjs', import.meta.url)); bar(); } catch (e) { console.error("bar failed:", e); } }); </script>
原理说明
Vite对静态import()语法会进行构建时解析,它会尝试在项目源码目录中查找文件,而非运行时的dist目录。使用new URL()的方式会让import变成运行时动态请求,直接从当前应用的根路径加载文件,无论开发还是生产环境都能正确访问public目录下的外部文件。
备选方案(不推荐)
如果一定要将文件放在dist目录而非public,可以在开发时配置Vite允许访问dist目录:
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { fs: { allow: ['dist'] // 允许开发服务器读取dist目录文件 } } })
但这种方式需要手动将.mjs文件复制到dist目录,开发时修改文件后无法自动刷新,不如public目录便捷。
内容的提问来源于stack exchange,提问作者baitendbidz

