在微前端qiankun中使用Vite你踩坑了吗?
哈喽,我是树酱。之前搭建的微前端体系已经稳步运行将近两年了,最近遇到一些童鞋反馈。之前据说qiankun并不支持Vite打包的应用,那是不是我就无法使用了?
是的,官方暂未有文档表明已经支持Vite。接下来我会从Vite聊起,然后一步步解析如何去解决在qiankun微前端体系中集成基于Vite构建的子应用.
1 为什么要用Vite?
在Vite没有诞生之前,我们前端大多都是基于 webpack 构建的,主要离不开以下两点:
本地开发(热更新HMR) 打包上线
webpack的核心简单概括就是将各类资源打包整合在一起,形成bundle
的能力。但是随着项目的不断迭代,慢慢演变成一个中大型项目,这时候你会发现打包时间太久了,换句话说构建效率变低了。
而在Bundle工具的演变过程中,我们见证了 webpack[1]、Rollup[2] 和 Parcel[3] 等工具,同时构建效率也在逐步提升,如下图所示👇
而伴随着浏览器对ES模块(ESM)逐步支持兼容,是不是有更快的方式可以解决构建问题?
那就是基于浏览器支持的 ESM import
特性实现的 bundless
, 通过利用浏览器进行模块间依赖加载,而不需要在编译时进行。
换句话说我们不再需要构建一个完整的 Bundle(下文我们称为:Bundless)。当我们修改文件时,浏览器只需要重新加载单个文件即可。
啊乐同学:那有哪些 Bundless 解决方案 ?
(见下文)Vite就是其一,回顾下Vite
的优势:👇
在开发模式下:基于
esbuild
预构建依赖(减少HTTP请求) + 浏览器自主加载对应的模块,热更新页面!在生产模式下:基于
Rollup
的打包,速度也有一定提升
你一旦体验到Vite的神速!你真的会停不下来 🚀
🍚 饭后思考:
esbuild不是比Rollup更快吗?生产模式下,为何不用esbuild构建?👉 参考答案[4]
如果是对于原生ESM不支持的浏览器,开发模式咋处理?👉 参考答案[5]
不是说好bundless?为何还要用
esbuild
预构建依赖呢?👉 参考答案[6]Bundless方案除了Vite之外,还有哪些?👉 参考答案[7]
Vite 的目标是要干掉 Webpack?👉 参考答案[8]
2. 微前端框架qiankun与Vite
通过上文,我们了解到使用Vite的优势。那是否
qiankun
支持基于vite构建的子应用集成呢?这里我们以vue3+vite
的demo为例
会遇到以下两个需要解决的问题:
开发模式:在开发环境下,如果我们使用 vite 来构建 vue3 子应用,基于vite的构建机制,会在子应的
html
的入口文件的script
标签上携带type=module
。而我们知道qiankun父应用引入子应用,本质上是将html做为入口文件,并通过import-html-entry
这个库去加载子应用所需要的资源列表Js、css,然后通过eval直接执行,而基于vite构建的js中import、export
并没有被转码,会导致直接报错(不允许在非 type=module 的 script 里面使用 import)生产模式:生产模式下,因为没有诸如webpack中支持运行时
publicPath
,也就是__webpack_public_path__
,换句话说就是vite不支持运行时publicPath,其主要作用是用来解决微应用动态载入的脚本、样式、图片等地址不正确的问题。
🌲 拓展阅读:
一开始import-html-entry
会过滤掉type=module
的文件,导致缺失js却直接eval最终执行出错,后期这个问题官方已经支持👇
Support of type=module and nomodule attribute in import-html-entry[9]
目前qiankun官网文档并没有基于Vue3+Vite构建的子应用打包的文档指引,但是我们可以在Github的Issue中找到一些解决方案,主要通过以下这两种方式解决
2.1 只解决生产模式的集成
我们可以通过对子应用vite配置的构建配置改造来实现
首先修改Vite.config.js
·中的build配置, 默认Vite的输出目标target
是module
,需改为esnext
然后在配置文件中引入 @rollup/plugin-html[10]
上图省略部分方法,详情请看本节末尾的Demo实例[11],代码实现的目的是为了构建html
文件作为子应用的入口,构建结果如下所示👇
其他环节跟基于Webpack的配置大致相同,这里不一一赘述
虽然这种方式针对生产模式可以实现集成,但是存在几个局限性:
我们知道为了让 qiankun
拿到子应用export
的生命周期函数,所以需要将子应用打包成umd
格式,而vite的code-splitting(代码分割)功能并不支持iife
和umd
两种格式,这会导致路由无法实现懒加载。
因为vite不支持运行时publicPath,只能在打包时写死
Base配置
图片最终会被打包成
base64
更详细的Demo集成例子:👉 app-vue-vite[12]
2.2 解决开发模式 + 生产模式的集成
单独解决生产模式的集成也不方便,毕竟很多时候需要我们在本地环节进行调试,那有什么方式可以同时让Vite支持这两种模式的集成呢?
Github上有一名开源作者开发了一款Vite插件叫vite-plugin-qiankun
,通过这个插件可以在qiankun下走通这两种模式。甚至保留了vite构建模块的优势
修改 Vite.config.js
修改子应用的 main.ts
,将生命周期mount
、bootstrap
、unmount
等通过插件函数renderWithQiankun
在其中暴露完成。其他配置与基于webpack构建的子应用相同
⏰ 注意事项:
qiankun官方是以 window.__POWERED_BY_QIANKUN__
来判断当前是否为qiankun环境下,而该插件引用之后是通过qiankunWindow.__POWERED_BY_QIANKUN__
来判断
🐸 局限性:
生产模式下依旧不支持 publicPath
, 需要将vite.config.js
中base
配置写死。导致多环境部署不便捷。无法像在webpack结合window.INJECTED_PUBLIC_PATH_BY_QIANKUN
+publicpath
来解决
更详细的Demo集成例子:👉 viteapp[13]
2.3 Vite对runtime publicpath的支持
目前在Vite官方文档没查阅到相关的配置,但在Github中找到一个插件vite-plugin-dynamic-publicpath[14]。如果你有更好的解决方案,也欢迎评论区留言
2.4 关于Vite的Dotenv配置
如果你从 vue-cli 切换到Vite 需要注意 Dotenv 命名的变化
vite
前缀是VITE_
,vue-cli
是VUE_APP_
获取方式也不一样,在 vite
是通过import.meta.env
,而在vue-cli
则是通过process.env
3.最后
如果你有其他解决方式,欢迎在评论区留言,也可以加我微信,我们一起喝茶🍵 讨论
你好,我是🌲 树酱,请你喝杯🍵 记得三连哦~
1.阅读完记得点个赞哦,有👍 有动力
2.关注公众号前端那些趣事,陪你聊聊前端的趣事
3.文章收录在Github frontendThings 感谢Star✨
参考资料
webpack: https://webpack.js.org/
[2]Rollup: https://rollupjs.org/
[3]Parcel: https://parceljs.org/
[4]👉 参考答案: https://cn.vitejs.dev/guide/why.html#why-not-bundle-with-esbuild
[5]👉 参考答案: https://cn.vitejs.dev/guide/#browser-support
[6]👉 参考答案: https://cn.vitejs.dev/guide/dep-pre-bundling.html
[7]👉 参考答案: https://cn.vitejs.dev/guide/comparisons.html
[8]👉 参考答案: https://www.zhihu.com/question/477139054/answer/2156019180
[9]Support of type=module and nomodule attribute in import-html-entry: https://github.com/umijs/qiankun/issues/507
[10]@rollup/plugin-html: https://github.com/rollup/plugins/tree/master/packages/html
[11]Demo实例: https://github.com/gongshun/qiankun-vue-demo/blob/feature/vite-child/app-vue-vite/vite.config.ts
[12]👉 app-vue-vite: https://github.com/gongshun/qiankun-vue-demo/tree/feature/vite-child/app-vue-vite
[13]👉 viteapp: https://github.com/tengmaoqing/vite-plugin-qiankun/tree/master/example/viteapp
[14]vite-plugin-dynamic-publicpath: https://github.com/jy0529/vite-plugin-dynamic-publicpath