自造微前端

起因是,我要在一个大型后台管理应用中剥离出一个管理模块,这样做的好处当然是可以单独开发和发布,但是坏处也有,比如部分全局变量污染,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插件,避免微前端环境下因路径问题导致运行时脚本加载失败。 至此,完成。

相关推荐
前端小D1 分钟前
JS核心底层
前端·javascript
SameX15 分钟前
ArkTS 做 5 态数据流 —— 咖啡电量的代谢状态机怎么驱动 UI
前端
Dovis(誓平步青云)16 分钟前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互
爱吃烧烤的技术渣19 分钟前
别再手写 CRUD 了:我把 Element Plus 的工作量压缩成一份列 schema
前端·vue.js
MetWeave22 分钟前
风的三十年:一个机场的风在变小吗:10,958 天的账(《风不是一个数》系列第 6 篇)
前端·数据可视化
青烟小生25 分钟前
前端实时协同编辑与 CRDT 冲突解决实战:从 OT 到 Yjs 底层原理(完整版)
前端
陈随易27 分钟前
神级 Skill,让 AI 用 HTML 代替 Markdown 回复
前端·后端·程序员
eidnxndrdkdns35 分钟前
PE 自校验的三个坑:恒失败、基址变化、以及被漏在外面的检测器
前端
90后的晨仔39 分钟前
解决 Android 构建报错:Unable to delete file .../file-map.txt
android·前端
Yuhano44 分钟前
W4. RAG 让 Agent 拥有"外部记忆"
前端·aigc