自造微前端

起因是,我要在一个大型后台管理应用中剥离出一个管理模块,这样做的好处当然是可以单独开发和发布,但是坏处也有,比如部分全局变量污染,UI不一致,状态通信传导等。虽然市面上已经有很多微前端方案了,比如qiankun等,但是这都需要对这个大仓库应用进行大动工,对原应用造成一些负载和可能意想不到的错误,非我所愿也,所以在这里我打算自己搞个简化的方案。 原应用是react+dva,

基座应用改造

我们设计的主应用核心是;

  1. 主应用不直接打包子应用的代码,而是运行时按需加载子应用的 JS 资源。
  2. 将子应用的路由注册到主应用的路由系统中,实现无缝跳转。
  3. 通过全局变量(window)共享库,避免重复加载,也可形成单例。 我们先改造子应用路由代码,为了解决刚才说的UI和一些重要库不一致问题,我们将直接暴露库到Window中,有:
  4. antd
  5. React主库 另外状态通信的问题,我们直接使用dva去修饰取得的子应用Com,并把部分方法如connect暴露到window中。

代码如下

ini 复制代码
import React from 'react';
import {
  Alert,
  Drawer,
} from 'antd';
import { connect } from 'dva';

window.React = React;
window.CONNECT = connect;
window.Antd_ZI = {
  Drawer,
  Alert,
};

const configUrl = {
  test: 'https://zi.test.cn',
  product: 'https://zi.prod.com',
};

const env = 'product';

const mapStateToProps = (state) => {
  const currentState = state.adManage;
  const { userAuth, userInfo } = state.app;
  return { ...currentState, userAuth, userInfo };
};

const insertScript = (e, isStyle = false) => {
  return new Promise((reslove, reject) => {
    const i = isStyle ? document.createElement('link') : document.createElement('script');
    isStyle || i.setAttribute('type', 'text/javascript');
    isStyle || i.setAttribute('src', e);
    isStyle && i.setAttribute('href', e);
    isStyle && i.setAttribute('rel', 'stylesheet');
    isStyle && i.setAttribute('type', 'text/css');
    function onload() {
      if (!(this.readyState && this.readyState !== 'loaded' && this.readyState !== 'complete')) {
        i.onload = null;
        i.onreadystatechange = i.onload;
        reslove();
      }
    }
    function onerror(ee) {
      reject(ee);
    }
    i.onreadystatechange = onload;
    i.onload = onload;
    i.onerror = onerror;
    document.querySelector('head').appendChild(i);
  });
};

function ERROR() {
  return '加载静态资源失败,请稍后重试';
}

const subappRoutes = [];

const AyncComponent = async (pathname) => {
  const id = pathname;
  // 子工程资源是否加载完成
  let ayncLoaded = false;
  if (subappRoutes[id]) {
    // 如果已经加载过该子工程的模块,则不再加载,直接取缓存的routes
    ayncLoaded = true;
  } else if (window.ziPLATFORMSLOT && window.ziPLATFORMSLOT[pathname]) {
    const res = await window.ziPLATFORMSLOT[pathname]();
    subappRoutes[id] = res.default;
    ayncLoaded = true;
    // return subappRoutes[id];
  } else {
    try {
      await insertScript(`${configUrl[env]}/js/index.js?_=${new Date().getTime()}`);
      if (window.ziPLATFORMSLOT && window.ziPLATFORMSLOT[pathname]) {
        const res = await window.ziPLATFORMSLOT[pathname]();
        subappRoutes[id] = res.default;
        ayncLoaded = true;
      }
    } catch (error) {
      console.log('加载js失败', error);
    }
  }
  return ayncLoaded ? connect(mapStateToProps)(subappRoutes[id]) : ERROR;
};

export default [
  {
    name: '资源管理',
    path: 'ziMedia',
    component: () => AyncComponent('ziMedia'),
  },
  {
    name: '黑名单管理',
    path: 'ziBlack',
    component: () => AyncComponent('ziBlack'),
  },
];

子应用改造

子应用中,入口文件我们需要定义publicPath保证资源的正确引用

javascript 复制代码
const configUrl = {
  test: 'https://zi.test.cn',
  product: 'https://zi.prod.com',
};

const env = 'product';
// webpack 的魔法变量,用于指定异步加载的 JS/CSS 文件的基地址(如 `import()` 动态导入的模块)
__webpack_public_path__ = `${configUrl[env]}/js/`;

const routes = {
  version: '1.0.0',
  ziMedia: () => import('./ziMedia/index'),
  ziBlack: () => import('./ziBlack/index'),
};

export default routes;

在子应用的页面中,我们基本不需要做改造,但是在webpack的配置中,我们在base中需要:

css 复制代码
{
    ...
    output:{
        ...
          libraryExport: 'default',
          library: 'ziPLATFORMSLOT',
          libraryTarget: 'umd',
          umdNamedDefine: true,
    },
    externals: {
      'react': 'React',
      'antd': 'Antd_ZI',
      // 'react-dom':'ReactDOM'
    }
}

在prod配置中,为了保证子js能正确加载执行,我们使用InlineChunkHtmlPlugin插件,避免微前端环境下因路径问题导致运行时脚本加载失败。 至此,完成。

相关推荐
之恒君2 小时前
寄生组合继承 vs ES6 类继承 深度对比
前端·javascript
涔溪2 小时前
整理vue3+ vite 开发经常会遇到的问题。
前端·vue.js·typescript
用户51681661458412 小时前
script 标签的异步加载:async、defer、type="module" 详解
前端·javascript
m0_471199632 小时前
【vue】dep.notify() 是什么意思?
前端·javascript·vue.js
威风的虫2 小时前
Vue3中的生命周期钩子函数
前端·javascript·vue.js
m0_471199632 小时前
【vue】vue2和vue3响应式原理区别
前端·javascript·vue.js
拜无忧2 小时前
纯css,顺时针3d旋转基座(摩天轮效应)
前端·css
奋斗猿3 小时前
从0到1开发跨平台桌面应用:Electron 实战全指南
前端·electron
之恒君3 小时前
script 标签中的 async 和 defer 的区别
前端·javascript