自造微前端

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

相关推荐
徐小夕27 分钟前
JitWord Office预览引擎:如何用Vue3+Node.js打造丝滑的PDF/Excel/PPT嵌入方案
前端·vue.js·github
晴殇i36 分钟前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
孟陬1 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
BER_c1 小时前
前端权限校验最佳实践:一个健壮的柯里化工具函数
前端·javascript
兆子龙1 小时前
别再用 useState / data 管 Tabs 的 activeKey 了:和 URL 绑定才香
前端·架构
sudo_jin1 小时前
前端包管理器演进史:为什么 npm 之后,Yarn 和 pnpm 成了新宠?
前端·npm
敲敲敲敲暴你脑袋1 小时前
写个添加注释的vscode插件
javascript·typescript·visual studio code
叁两2 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
golang学习记2 小时前
GitLens 十大神技:彻底改变你在 VS Code 中的 Git 工作流
前端·后端·visual studio code
SuperEugene2 小时前
后台权限与菜单渲染:基于路由和后端返回的几种实现方式
前端·javascript·vue.js