Open alvin0216 opened 2 years ago
是的,这在README 的“限制”章节那一部分提到了,其本质是rollup的动态加载是基于blob全量加载本地模块导致的。
一种临时解决方案是提前生成所需要的所有远程模块的配置文件,然后声明import,保证远程模块会被加载到本地。
这个方案在我最近的一个项目 vue-page-builder中使用了,其中的importStr
就是提前注入所需要的远程模块的声明。
vue-page-builder是一个依赖于vite-plugin-remote-module的项目,我正在该项目中持续迭代这个插件,希望能真正用在实际项目中,感谢你的反馈。如果你有更好的想法和思路,欢迎与我沟通
实测 UMD 可以解决动态加载问题,微前端 & webpack MF(只能用于 webpack 构建的环境) 都有接入成本...
抱歉我对微前端和 webpack module federation 了解并不是非常深入
在我的理解中,module federation 本质上也需要远程项目将对应的模块 暴露出来,比如
new ModuleFederationPlugin({
name: 'app2',
filename: 'remoteEntry.js',
// 声明远程项目暴露出来的模块
exposes: {
'./App': './src/App',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
})
然后在本地项目中配置远程模块的映射
new ModuleFederationPlugin({
name: "app1",
remotes: {
app2: "app2@[app2Url]/remoteEntry.js",
},
shared: {react: {singleton: true}, "react-dom": {singleton: true}},
})
最后在本地项目中通过动态模块的方式来加载远程模块
const RemoteApp = React.lazy(() => import("app2/App"));
这里的import路径也不能使用变量。
SystemJs可以加载动态变量的远程模块,但无法处理JSX、Vue等未编译的模块。
理论上也需要将UMD模块通过`vite-plugin-remote-module``下载到本地之后,才能成功打包,你说的”UMD 可以解决动态加载问题“具体是什么意思呢?
其实也存在一些问题的,最近在研究这一块
哦哦,vite-plugin-remote-module
的目标是加载远程的模块源码,而不是打包后的模块产物。
这样做的目的是让远程模块之间也可以互相依赖,减少更新模块前的编译发布耗时,最终在本地统一打包。
应用场景主要是我在vue-page-builder 这种低代码平台项目尝试,在传统的基于组件的低代码平台:
我的想法是把这些组件都放在服务端,最终在访问页面前,在服务端通过remote-module
加载该页面需要的远程模块,然后进行编译,最终输出静态HTML文件和1个js bundle文件就可以了。
感觉是我们对加载远程模块的用途考虑不太一样,哈哈
我的想法是把这些组件都放在服务端,最终在访问页面前,在服务端通过remote-module加载该页面需要的远程模块,然后进行编译,最终输出静态HTML文件和1个js bundle文件就可以了。
实际上开发完毕之后 也是需要部署你那个低代码平台吗?我看是最终也打包到本地才能使用的,这和 lazy load 没什么特别大的区别哦
是的,这个加载多份资源的问题就是我要处理的,需要侵入打包环节,将所有资源打包合并到一起。
在我之前接触到的类似项目里面,我们的方案是根据模块依赖的manifest文件,手动拼接相关的模块代码。类似于
http://server/combojs?sources=demo1.js,demo2.js,demo3.js
这个url请求到服务器,由combo服务器拼接sources文件,然后返回一个完整的js文件。
由于在低代码平台下面是根据页面配置输出的vue文件再编译,理论上是可以直接将异步模块替换成同步模块的。这样就无需通过 lazy load来加载了
最近有时间了我会把这个功能实现,到时候再@你
我刚刚尝试了一下
页面配置大概是这样的
在服务端请求这个页面后,首先解析这个页面用到的所有远程组件,
export default function json2sfc(vnode: string) {
// 解析页面需要的动态模块...
// 提前加载需要的动态模块
let importStr = ''
let widgetMapStr = '{'
Array.from(list).forEach((url: string, index: number) => {
const name = `RemoteWidget${index}`
importStr += `import ${name} from '@remote/${url}'\n`
widgetMapStr += `'${url}':${name},`
})
widgetMapStr += '}'
// 返回sfc 组件内容
}
输出的页面组件大概是这个样子的
这样所有动态加载的组件都会变成静态加载的组件了,最后得到的页面就是只有一个js bundle了
也就达到了我最初的目的
这个项目目前只是一个想法的尝试,距离真正线上使用应该还有很多工作要处理
我刚刚尝试了一下
页面配置大概是这样的
在服务端请求这个页面后,首先解析这个页面用到的所有远程组件,
export default function json2sfc(vnode: string) { // 解析页面需要的动态模块... // 提前加载需要的动态模块 let importStr = '' let widgetMapStr = '{' Array.from(list).forEach((url: string, index: number) => { const name = `RemoteWidget${index}` importStr += `import ${name} from '@remote/${url}'\n` widgetMapStr += `'${url}':${name},` }) widgetMapStr += '}' // 返回sfc 组件内容 }
输出的页面组件大概是这个样子的
这样所有动态加载的组件都会变成静态加载的组件了,最后得到的页面就是只有一个js bundle了
也就达到了我最初的目的
- 远程使用数据库里面的组件列表
- 最后按页面配置生产单个bundle文件
这个项目目前只是一个想法的尝试,距离真正线上使用应该还有很多工作要处理
这个截图有演示环境吗?
比如有 demo1 demo2 两个组件,动态加载时会下载到 .remote-modules,但是没有访问过的就没有记录。 打包之后,没有缓存到 .remote-modules 的文件,加载则会报错