Vite微应用如何接入qiankun

qiankun是一个很流行的微前端解决方案。之前我也详细的分析过qiankun的原理,感兴趣的可以看看。

Vite是当下比较流行的构建工具,它对标的是webpack,并作为Vue3脚手架的默认工具替代了老版vue-cli中的webpack。当然,Vite不仅仅能使用在Vue中,React+Vite也是很好用的。它的特点就是开发模式下运行特别“”,这个得益于浏览器对ESM的原生支持。

既然两个都是流行的解决方案,那么是否可以把它们结合在一起使用呢?比如React+Vite+qiankun架构的项目,把它作为一个微应用接入到主应用中,同时微应用还保留ESM的特性?很可惜,直接接入是不行的。

存在哪些问题?

Vite构建的应用,开发环境下使用ESM的方式加载脚本。而到目前为止,qiankun还没有增加对ESM的官方支持。如果我们在加载基于Vite的微应用时,会出现一系列问题:

  • 直接报错:Cannot use import statement outside a module

    原因:ESM的脚本内部直接使用import xxx from 'xxx'语法,Vite开发模式下并不会对其转码。而这样的语法是无法在qiankun里直接fetch然后eval的,它只能在ESM中使用。即通过<sciprt type="module>"声明的入口脚本。

  • ESM无法导出qiankun生命周期函数

    原因:qiankun需要微应用打包为umd格式。这种情况下,qiankun对微应用的脚本进行eval操作后,可以在js沙箱*(window.proxy[appName])*下,获取到导出的所有生命周期函数。而对于ESM格式的脚本,qiankun无法直接获取到生命周期。

  • 应用内相对路径的静态资源出现404

    原因:微应用独立运行时,相对路径从自己的根开始。但当微应用被加载到主应用中时,相对路径从主应用的根开始。此时静态资源会出现404。webpack支持运行时publicPath技术,即__webpack_public_path__。所以需要进行如下设置:

    // public-path.js
    if (window.__POWERED_BY_QIANKUN__) {
         
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    

    这代表如果微应用被加载到主应用中,则publicPath会设置为qiankun注入的变量,这个变量就是微应用的地址。这样所有的静态资源都会从微应用的地址获取,不会404。而独立运行时也不会404。

    但是Vite是不支持运行时publicPath技术的。我们得去手动设置静态的publicPath。

  • 不支持多环境部署

    原因同上,由于Vite不支持在运行时,动态修改publicPath。所以只能手动设置静态的publicPath。但是这样的话多环境就得配置环境变量.env,分开构建并部署。不能实现一次构建,多环境运行。

对于这些问题,qiankun没有解决,我们只能自己写个插件,看看在插件的帮助下,能否让Vite微应用,既能保留ESM特性,又能接入微前端的能力。

实现解决方案

针对于上面的这几个问题,最好的肯定是qiankun官方去支持,对ESM和普通script分开去处理。但是它没有做,那只能我们去做了。我们也不可能大量的改项目源码去支持。那最好的就是开发一个Vite插件,让Vite插件去modify我们的代码。

初始化一个插件项目,然后一步一步的解决问题。

import导入ESM并运行

首要的问题就是ESM的脚本无法直接被fetch然后eval。但是通过测试发现,动态的import()语法,可以做到不报错正常执行。即如下代码:

// <==静态import语句会报错:`Cannot use import statement outside a module`
const scriptText = `
  import "./main.js";
`;
evalSandox(scriptText);

// ==>动态import()语句不报错,正常执行
const scriptText = `
  import("./main.js");
`;
evalSandox(scriptText);

那么,我们的插件就需要对index.html下,所有<script type="module" src="xxx">的ESM入口脚本进行转换。转换为内联脚本:

<script>
  import(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ + "xxx")
</script>

由于我们改为手动控制ESM脚本的导入,所以需要自己追加base路径。否则它会从主应用下找脚本,导致404.

ESM导出应用生命周期

qiankun需要应用导出生命周期函数。我们需要插件做到:把生命周期函数绑定到window.proxy[appName]下。

在上一步中,我们使用动态import语法导入了模块,正好我们可以从模块中获取到生命周期。这样模块中仍然保持qiankun官方的设置方法:使用export导出生命周期。

// 插件中
import("./main.js").then((mod) => {
   
  mod.bootstrap
  mod.mount
  mod.unmount
})

// main.js模块中
// ...
export function bootstrap() {
    /* */ }
export function mount(props) {
    /* */ }
export function unmount() {
    /* */ }

在模块导入后再设置到window上肯定不行的,因为动态import语法是异步的。而qiankun需要在所有入口脚本执行完毕后,同步获取生命周期函数。类似于下面:

// 执行所有脚本
execScripts(entry, global);
// 获取生命周期
const scriptExports = global[app.name];
const {
    bootstrap, mount, unmount } = scriptExports;

所以如果在导入后再设置到window上,直接就undefined报错了。那怎么办?看到qiankun的文档中,生命周期函数可以是异步的。这就有操作空间了。我们可以先返回一个虚拟的生命周期对象,生命周期函数内,如果模块已加载那调用真实的生命周期,否则返回Promise,这个Promise会在模块加载后被resolve。简化代码如下:

// 插件内
let realModule = null
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值