前端微应用初探

背景

最近,工作中遇到一个问题:手头有两个项目,其中有几个模块界面功能都是一模一样的,所以想着是否可以共用一套代码。
发现有一些解决方案:

  1. 通过iframe嵌套页面;
    缺点:遇到iframe高度控制问题,iframe是不能自适应变更的,所以需要在子应用里进行动态改变iframe的高度(包含各种数据渲染及图片加载完成的监听等),方法比较复杂,代码也会很冗杂;浏览器状态不同步,刷新后iframe状态丢失;
    优点:但是没有跨域问题,样式和js隔离;
  2. 通过qiankun(使用的是single-spa)配置微应用实现:
    缺点:有跨域问题,需要部署在统一的域名下,而且父子应用之间的元素选择器的样式会互相影响;
    优点:好处是父子应用里的代码修改不多,子应用中有相应标识能知道当前是子应用还是说独立访问。

    qiankun基本原理

    qiankun框架的原理是使用fetch获取子应用的所有静态资源,然后将子应用的界面内容放入配置好的容器内,父子应用的内容不会互相隔离。

    基本配置

    基本安装和配置点击链接

    遇到的一些问题

    开发问题

基本描述:父应用使用vue技术栈,子应用使用create-react-app创建的项目(react v3.x),并且路由模式是hash模式,使用react-scripts启动。
开发时是在本地启动联调;

  1. 跨域问题
    qiankun通过fetch引入静态资源,父应用和子应用开发环境并不在同一个端口,解决方法有两个:
  • 1.1 本地启动nginx在同一个端口下分配不同前缀进行代理转发;
  • 1.2 在子应用的启动项里增加允许跨域相关配置(本人子应用是webpack,修改config-overrides.js)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    module.exports = {
    webpack: (config) => {
    // 微应用的包名,这里与主应用中注册的微应用名称一致
    config.output.library = `${name}`;
    // 将你的 library 暴露为所有的模块定义下都可运行的方式
    config.output.libraryTarget = 'umd';
    // 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
    config.output.jsonpFunction = `webpackJsonp_${name}`;
    return config;
    },
    // 支持开发环境跨域
    devServer: configFunction => {
    return function (proxy, allowedHost) {
    const config = configFunction(proxy, allowedHost);
    config.headers = {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Headers":"content-type",
    "Access-Control-Allow-Methods":"DELETE,PUT,POST,GET,OPTIONS"
    };
    return config;
    };
    },
    };
  1. 子应用忽略一些脚本的加载
    qiankun默认会加载子应用的所有脚本,如果有不需要在微应用中加载的脚本,可以在script标签加上ignore属性
    <script src="xxx.js" ignore></script>
  2. 父子应用polyfill问题
    父子应用启动时只能存在一个polyfill,所以在开发时可以去掉其中一个。

    部署问题

  3. 部署后的主应用中注册的微应用entry配置需要同步变更为可访问地址;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    registerMicroApps([
    {
    name: 'microApp',
    entry: '//microApp.xxx.com/microApp/index.html',
    activeRule: '/microApp',
    props: msg,
    container: '#microAppContainer',
    },
    ]);