umi+qiankun如何使用Webpack5 模块联邦题】

前言

官方文档:https://www.webpackjs.com/

通过阅读文档,使用webpack 模块联邦有两个问题需要解决:

  1. 异步导入
  2. react实例唯一,依赖共享

​ 如果单纯在umi中使用模块联邦,可以使用umi插件的方式异步导入入口,依赖也可以使用shared来共享,但是这种方式在qiankun下使用会不兼容,在此我们主要说下umi+qiankun的思路, 对umi插件有兴趣可以翻下之前发的一个umi的文章

思考:
1、异步导入如何解决?

​ 如果了解qiankun会知道,qiankun的HTMl Entry 是用import-html-entry实现的,其本质就是异步加载远端文件的方式,所以子应用使用模块联邦其实不需要关注异步导入这个问题

2、react 实例唯一

这个我们也可以用shared来解决,同样我们也可以换个思路,既然所有的子应用要使用同一个react实例,那我们直接引用用一个react不就可以了

方案:
1、技术栈

egg + umi + qiankun + webpack5

2、如何实现
1、 架构设计

项目整体分为项目入口、基座(主应用)和子应用三大部分

1.1 项目入口

项目入口使用egg项目启动,配置路由如下:

router.get('/*', controller.home.index);

然后设置home的colltroller

  ctx.body = `
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1,             user-scalable=no"
    />
    <script>
      window.routerBase = "/";
    </script>
    <script>
      window.publicPath = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ || "/";
    </script>
  
    <script>
      //! umi version: 3.5.23
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  </body>
</html>

入口文件引用react和react-dom供整个项目使用,这样保证项目整体的react实例是唯一的

1.2 基座

配置umi项目配置

export default defineConfig({
  base:'/',
  alias:{
    '@':'../src'
  },
  hase:false,
  title:false,
  qiankun:{
    master:{
      apps: [
        {
          name: 'app1', // 唯一 id
          entry: 'http://localhost:2001/index.html', // html entry
        },
        {
          name: 'app2', // 唯一 id
          entry: 'http://localhost:2002/index.html', // html entry
        },
      ],
    }
  },
  routes: [
    {
      path: 'app1',
      microApp: 'app1',
    },
    {
      path: 'app2',
      microApp: 'app2',
    },
  ],
  fastRefresh: {},
  externals: {
    react:'var window.React',
    'react-dom':'var window.ReactDOM'
  },
  mountElementId:'root',
  publicPath:'./',
});

注意 externals 配置要结合scripts一起使用,scripts会导入远程地址,但这里我们不需要每个项目导入,所以不用scripts

1.3子应用-组件提供方

使用umi启动项目之后配置webpack5模块联邦

const { ModuleFederationPlugin } = require("webpack").container;
....
webpack5:{},
externals: {
    react:'var window.React',
    'react-dom':'var window.ReactDOM'
  },
 chainWebpack(memo) {
    memo.output.publicPath('auto');
    memo
      .plugin('mf')
      .use(ModuleFederationPlugin, [{
        name: "layout",
        library: { type: 'umd', name: 'layout' },
        filename: 'remoteLayout.js',
        exposes: {
          "./Layout": './src/pages/index',
        },
      }])
  },
.....

注意: webpack5需要配置,这样umi才会使用webpack5 .其他模块联邦配置可参考官网

1.4 子应用-组件使用方
....
webpack5:{},
externals: {
    react:'var window.React',
    'react-dom':'var window.ReactDOM'
  },
  chainWebpack(memo) {
    memo.output.publicPath('auto');
    memo
      .plugin('mf')
      .use(ModuleFederationPlugin, [{
        name: "qiankun1",
        filename: 'remoteEntry.js',
        remotes: {
          mfLayout: 'layout@http://localhost:3000/remoteLayout.js',
        },
      }])
  },
...  
  

至此umi+ qiankun+ wepack5模块联邦 已经配置完成,那模块联邦都是写在umi配置里面,如果要动态导入该怎么处理呢?

1.5 模块联邦动态导入

a.将我们打包好的组件文件添加在入口文件,以script的方式加载js

 <script src="http://localhost:3000/remoteLayout.js" ></script>

c. 子应用使用

import React from 'react'
// 动态导入联邦组件
function loadComponent(scope: string, module: string) {
  return async () => {
    await __webpack_init_sharing__('default');
    const container = window[scope];
    await container.init(__webpack_share_scopes__.default);
    const factory = await window[scope].get(module);
    const Module = factory();
    return Module;
  };
}
// 懒加载组件
const componentCache = new Map();
export const getFederatedComponent = (remoteUrl: string, scope: string, module: string) => {
  const key = `${remoteUrl}-${scope}-${module}`;
  const Comp = React.lazy(loadComponent(scope, module));
  componentCache.set(key, Comp);
  return {Comp}
};

export default function IndexPage() {
  const { Comp: FederatedComponent } = getFederatedComponent('http://localhost:3000/remoteLayout.js', 'layout', './Layout');
  return (
    <div>
      <React.Suspense fallback="">
        <FederatedComponent/>
      </React.Suspense>
      <h1>Page index</h1>
    </div>
  );
}

为什么这么设置可以实现导入呢?这篇文章可以为我们解惑:https://github.com/Vincent0700/learning-webpack/blob/master/docs/Webpack%E6%A8%A1%E5%9D%97%E8%81%94%E9%82%A6%E5%8E%9F%E7%90%86.md

文章描述了远程模块的加载步骤:

  1. 下载并执行 remoteEntry.js,挂载入口点对象到 window.app1,他有两个函数属性,initgetinit 方法用于初始化作用域对象 initScope,get 方法用于下载 moduleMap 中导出的远程模块。
  2. 加载 app1 到本地模块
  3. 创建 app1.init 的执行环境,收集依赖到共享作用域对象 shareScope
  4. 执行 app1.init,初始化 initScope
  5. 用户 import 远程模块时调用 app1.get(moduleName) 通过 Jsonp 懒加载远程模块,然后缓存在全局对象 window[‘webpackChunk’ + appName]
  6. 通过 webpack_require 读取缓存中的模块,执行用户回调

其实我们刚才的loadComponent 函数就是手动实现了这些步骤,加载、创建、执行

结语:

至此 umi + qiankun + webpack5模块联邦的实现已经写完了,技术有限,不当之处还望多指教…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值