qiankun使用webpack5 module federation实践

4,638 阅读3分钟

qiankun

qiankun 是一个基于 single-spa 的微前端实现库,微应用能够独立开发独立部署。

什么是微前端

Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

微前端架构具备以下几个核心价值:

  • 技术栈无关
    主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立部署
    微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

    在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

  • 独立运行时
    每个微应用之间状态隔离,运行时状态不共享

Module Federation(模块联邦)

模块联邦也是一种微前端,可以颗粒化到单页应用或者组件。 每个字应用单独构建,主应用在运行时通过容器加载远程模块即子应用的构建。主应用自动使用子应用当前版本的最新资源。

实例,项目均接入qiankun

项目mf1, 提供共享模块(qiankun子项目)

mf1 共享模块为src/components/Button

import React from 'react';
const Button =() => {
  return (<div><button>我是mf1的button</button></div>);
}
export default Button;

mf1 的config 配置

const { ModuleFederationPlugin } = require("webpack").container;
const { REACT_APP_ENV, NODE_ENV } = process.env;
const publicPath = NODE_ENV === 'production' ? '/publicPath' : '/';

/**
 * dev 环境
 * publicPath: '/qiankun/zeusCI/', 则必须注释掉 memo.output.publicPath('auto')项目才能正确运行。 MF exposes不可用
 * publicPath: '/', 则无须注释掉 memo.output.publicPath('auto')。 MF exposes可用
 * 建议: dev 环境publicPath改为 '/'
 */
 
export default defineConfig({
  base: '/xxx',
  publicPath, 
  qiankun: {
    slave: {
    }
  },
  antd: {},
  dva: {
    hmr: true,
  },
  history: {
    type: 'hash',
  },
  dynamicImport: {
    loading: '@/components/PageLoading/index',
  },
  mfsu:{}, // umi3.5.13版本引入,加速构建
  webpack5: {},
  chainWebpack(memo) {
    memo.output.publicPath('auto'); // 请看上面注释
    memo
      .plugin('mf')
      .use(ModuleFederationPlugin, [{
        name: "mf1", // 该名称必须与入口名称相匹配

        filename: 'remoteEntry.js',
        exposes: {
          "./Button": './src/components/Button/index', // 共享模块
        },
       
      }]);
     
  },
  
  targets: {
    ie: 11,
  },
  // umi routes: https://umijs.org/docs/routing
  routes,
  // Theme for antd: https://ant.design/docs/react/customize-theme-cn
  theme: {
    'primary-color': defaultSettings.primaryColor,
  },
  title: false,
  ignoreMomentLocale: true,
  proxy: proxy[REACT_APP_ENV || 'dev'],
  manifest: {
    basePath: '/xxx',
  },
  exportStatic: {},
  esbuild: {},
});

项目2,引入mf1共享模块并使用(qiankun子项目)

项目2 page/index

import styles from './index.less';
import React from 'react'
import {Card} from 'antd'
const Mf1Button = React.lazy(() => import("mf1/Button"));
export default function IndexPage() {
  return (
    <div>
      <Card title="module federatino 应用">
       <React.Suspense fallback='loading'>
        <Mf1Button />
      </React.Suspense>
      </Card>
    </div>
    </div>
  );
}

项目2 config配置

qiankun: {
    slave: {}
  },
  antd: {},
  dva: {
    hmr: true,
  },
  plugins: [
  ],
  locale: {
    // default zh-CN
    default: 'zh-CN',

    // default true, when it is true, will use `navigator.language` overwrite default
    baseNavigator: true,
  },
  dynamicImport: {
    loading: '@/components/PageLoading/index',
  },
  
  webpack5: {},
  chainWebpack(memo) {
    memo.output.publicPath('auto');
    memo
      .plugin('mf')
      .use(ModuleFederationPlugin, [{
        name: "submodule",
       
        filename: 'submodule.js',
        exposes:{
          "./ProductList": './src/components/ProductList/index'
        },
        remotes: { // 引用远程模块
        // dev环境 "mf1@//localhost:7001/remoteEntry.js"
          mf1: "mf1@//xxxx.com/publicPath/remoteEntry.js" 
        },
        
      }])
  },

项目2启动

image.png

注意⚠️

共享模块不允许使用hooks

image.png

remotes 不需要加https或者http

image.png

exposes 必须为 "./Button": "...."

-   'Button': './src/components/Button'
+   './Button':'./src/components/Button'

image.png

总结

模块联邦提供单独构建单独部署,主应用只在运行时加载。主体应用程序将常用库定义为共享模块,以避免在页面构建中出现重复。相对npm包有很大的便捷性。 但是和qiankun混搭时,实操效果不佳。目前来看项目中采用MF,建议使用CRA初始化项目,或者emp架构。

以上个人拙见,欢迎指教。