微应用说明文档

-
2025-06-30

目录结构

一个基础的 微应用 项目大致是这样的

|-- .env

|-- config-overrides.js

|-- package.json

|-- yarn.lock

|-- build

|-- public

|-- node_modules.zip

|-- src

|-- api
        |-- assets
        |-- components
        |-- pages
        |-- router
        |-- store
        |-- styles
        |-- utils
        |-- index.js
        |-- index.less

根目录

package.json    包含插件和插件集

.env   环境变量

比如

PORT=9999

WDS_SOCKET_PORT=9999

yarn.lock

依赖包版本锁定文件

config-overrides.js

配置文件,包含webpack功能与插件的配置

node_modules.zip

项目的依赖压缩包,解压即可使用

build目录

执行 npm run build 后,产物默认会存放在这里

public目录

此目录下所有文件会被 copy 到输出路径。

/src 目录

api目录      与后端接口交互文件夹

assets目录   静态文件存放文件夹

components目录   公共组件存放文件夹

pages目录    业务代码存放文件夹

router目录    工程路由定义文件夹

store目录     工程 全局状态管理 文件夹

styles目录    工程 全局样式 文件夹

utils目录

工程工具函数文件夹

index.js

工程 入口文件

index.less

工程整体 样式文件

#项目基础环境要求
NODE :12.22.10及以上

NPM :最新的即可

配置手册

微应用打包工具 config-overrides.js 文件配置如下

const packageName = require('./package.json').name
const { override, fixBabelImports, addLessLoader, overrideDevServer, watchAll, addWebpackPlugin } = require('customize-cra')

module.exports = {
 'webpack': override(
   (config) => {
     config.output.library = `${packageName}`
     config.output.libraryTarget = 'umd'
     config.output.publicPath = '/';
     config.output.jsonpFunction = `webpackJsonp_${packageName}`
     return config
   },
   addLessLoader({
     lessOptions: {
       javascriptEnabled: true,
       modifyVars: { '@primary-color': '#3366ff' }
     },
   }),
 ),
 'devServer': overrideDevServer(
   (config) => {
     config.headers = config.headers || {}
     config.port = 9999;
     config.headers['Access-Control-Allow-Origin'] = '*'
     return config
   },
   watchAll()
 )
}

src/index.js 入口文件配置如下

import React from "react";
import ReactDOM from "react-dom";
import { ConfigProvider, Drawer, Modal, Button } from "antd";
import zhCN from "antd/lib/locale/zh_CN";
import { BrowserRouter, Route, HashRouter } from "react-router-dom";
import PubSub from 'pubsub-js'
import "./styles/curd.less"
import "./index.less";
class App extends React.Component {
   state = {
     flowModalShow: false,
     modalData:{}
   }
 
 closeFlowModalShow = () => {
   this.setState({ flowModalShow: false })
   this.state.modalData.unmountSelf()
 }
 componentDidMount() {
   PubSub.subscribe('modalData', (msg, data) => {  // 订阅事件
     if (data) {
       // 手动加载应用打开弹窗
     }
   }
 }
}
 
function render(props) {
 ReactDOM.render(
   <div className="developMaster">
     <BrowserRouter basename='/base/develop'>
       <ConfigProvider locale={zhCN}>
         <App />
       </ConfigProvider>
     </BrowserRouter>
   </div>,
   document.getElementById("microRoot")
 );
}
if (!window.__POWERED_BY_QIANKUN__) {  // 判断是不是 微前端(qiankun) 环境
 render();
}
function renderPatch(props) {
 PubSub.publish('modalData', props) // 发送广播事件
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap(props) { }
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
 render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
 ReactDOM.unmountComponentAtNode(document.getElementById("microRoot"));
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
 renderPatch(props);
}

启动项目

// 解压  node_modules.zip 

// 执行  npm start

// 访问  http://localhost:9999 或者将微应用注册到主应用直接访问主应用

// 打包  npm run build 打包代码 -> 部署上线 ( 参考部署文档 )

项目开发

在 src/index.js 入口文件中这样操作

import React from "react";
import ReactDOM from "react-dom";
import { ConfigProvider, Drawer, Modal, Button, message } from "antd"; // 引入 message 进行提示配置
import { BrowserRouter, Route, HashRouter } from "react-router-dom";
import zhCN from "antd/lib/locale/zh_CN";
import ResultsTheShop from './pages/ResultsTheShop'  // 引入开发的组件
class App extends React.Component {
 componentDidMount(){
   message.config({
     maxCount:1  // 设置 消息提示数量 最大为 1 个
   })
 }
 render() {
   return (
       <div>
         <Route path="/ResultsTheShop" component={ResultsTheShop} /> <!-- 在这里写自己的业务组件页面即可 -->
     </div>
   )
 }
}
function render(props) {
 ReactDOM.render(
   <div className="developMaster">
     <BrowserRouter basename='/base'>
       <ConfigProvider locale={zhCN}>
         <App />
       </ConfigProvider>
     </BrowserRouter>
   </div>,
   document.getElementById("microRoot")
 );
}
if (!window.__POWERED_BY_QIANKUN__) {
 render();
}

在 src/api/api.js 文件里修改与后端交互的 网关地址 代码如下

import axios from "axios";
const publicIp = "http://27.196.10.30/"  // 项目的网关地址更改为 业务网关地址
const instance = axios.create({    //创建axios实例,在这里可以设置请求的默认配置
   timeout: 180000, // 设置超时时间180s
   baseURL: publicIp   //根据自己配置的反向代理去设置不同环境的baeUrl
})
// 文档中的统一设置post请求头。下面会说到post请求的几种'Content-Type'
instance.defaults.headers.post['Content-Type'] = 'application/json'
/** 添加请求拦截器 **/
instance.interceptors.request.use(config => {
   config.headers['token'] = sessionStorage.getItem('token') || ''
 // 在这里:可以根据业务需求可以在发送请求之前做些什么:例如我这个是导出文件的接口,因为返回的是二进制流,所以需要设置请求响应类型为blob,就可以在此处设置。
   return config
}, error => {
   // 对请求错误做些什么
   return Promise.reject(error)
})
/** 添加响应拦截器  **/
instance.interceptors.response.use(response => {
    hide()
 if (response.code === '200') {     // 响应结果里的statusText: ok是我与后台的约定,大家可以根据实际情况去做对应的判断
   return Promise.resolve(response.data)
 } else {
   message.error('响应超时')
   return Promise.reject(response.data.message)
 }
}, error => {
 return Promise.reject('请求超时, 请刷新重试')
})

/* 统一封装get请求 */
export const get = (url, params, config = {}) => {
   return new Promise((resolve, reject) => {
       instance({
           method: 'get',
           url,
           params,
           ...config
       }).then(response => {
           resolve(response)
       }).catch(error => {
           reject(error)
       })
   })
}
/* 统一封装post请求  */
export const post = (url, data, config = {}) => {
   return new Promise((resolve, reject) => {
       instance({
           method: 'post',
           url,
           data,
           ...config
       }).then(response => {
           resolve(response)
       }).catch(error => {
           reject(error)
       })
   })
}

非 webpack 构建的微应用

接入之前请确保你的项目里的图片、音视频等资源能正常加载,如果这些资源的地址都是完整路径(例如:http://27.196.10.156:18770/static/img/logo.png),则没问题。如果都是相对路径,需要先将这些资源上传到服务器,使用完整路径。

接入非常简单,只需要额外声明一个 script,用于 export 相对应的 lifecycles. 例如:

声明 entry 入口

 <!DOCTYPE html>
   <html lang="en">
   <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Purehtml Example</title>
   </head>
   <body>
     <div>
       Purehtml Example
     </div>
   </body>
   <script src="http://绝对地址/entry.js" entry></script>
</html>

在 entry js 里声明 lifecycles

const render = ($) => {
   //可选择
   return Promise.resolve();
 };
 ((global) => {
   global['purehtml'] = {
     bootstrap: () => {
       console.log('purehtml bootstrap');
       return Promise.resolve();
     },
     mount: () => {
       console.log('purehtml mount');
       return render($);
     },
     unmount: () => {
       console.log('purehtml unmount');
       return Promise.resolve();
     },
   };
 })(window);

目录