记录微前端micro-app的配置

4,780 阅读5分钟

2022.04.18更新:
1、完善了通过基座的侧边栏切换路由,达到切换子应用页面的效果:

我遇到了一个大坑,我的路由用的react-router v6,可能是我配置的react-router集中式路由有问题,我在基座应用切换路由,子应用总是显示404。我最终是把react-router切换回了v5版本

但即使使用了react-router v5版本,我在基座的侧边栏切换路由还是不好使的,是因为micro-app本身是不支持应用之间自动跳转的,包括基座应用无法通过控制自身的路由去控制子应用的显示:
zeroing.jd.com/micro-app/d…

最终使用了issues中的这个方法:

useEffect(() => {
    window.dispatchEvent(new PopStateEvent('popstate', { state: null }));
}, [location.pathname]);

2、把子应用改成了history路由
我的项目侧边栏菜单都是后端传给前端的,为了保持菜单格式一致(如果子应用使用hash路由,那么菜单格式为 /chilApp#/web/order 这种),最终改成了基座+子应用都使用history路由

注:github已更新

2022.02.17更新:
1、完善了webpack history路由的配置,解决二级页面刷新空白的问题
2、子应用如果引入了地图script js,要script.setAttribute("ignore", "true")
3、完善数据通信,处理子应用登录失效的情况

原文

介绍:

基座应用react

文章发布时,涉及到的依赖包版本:

{
  "@micro-zoe/micro-app": "^0.8.4",
}

文章默认您已经大致浏览过micro-app官方文档,所以最好请先阅读官方文档:cangdu.org/micro-app/d…

我目前大多数的项目都是hash路由,本来想着基座和子应用都配置成hash路由,但是我没找到官方的例子,自己也没试出来。。。
最终选择了 基座应用history路由、子应用hash路由的方式 (注:后面已全部改为history路由)

更多实现方式:cangdu.org/micro-app/d…

下面说一下 需要做的修改:

基座应用

1、修改路由模式为history

webpack搭建的

import { BrowserRouter, HashRouter, useRoutes } from 'react-router-dom'
...
function App() {
  return (
    <BrowserRouter>
      <RouteElement />
    </BrowserRouter>
  )
}
export default App

webpack.config.js -- 2022.02.17更新
config.js一共需要改两个地方:

...
output: {
  ...
  publicPath: '/', // history路由
  ...
},
...
...
devServer: {
  ...
  historyApiFallback: true, // history路由
},

如果是umi的项目

需要查文档修改config

2、安装@micro-zoe/micro-app

npm install @micro-zoe/micro-app

3、入口文件

引入microApp并start

import ReactDOM from 'react-dom'
import App from './App'
import microApp from '@micro-zoe/micro-app'

microApp.start({
  // 本地启动时 sockjs-node报错 要不然会一直刷新
  plugins: {
    modules: {
      app1: [
        {
          loader(code) {
            if (process.env.NODE_ENV === 'development' && code.indexOf('sockjs-node') > -1) {
              code = code.replace('window.location.port', 8052) // 这里需要修改成子应用的端口
            }
            return code
          },
        },
      ],
    },
  },
})
ReactDOM.render(<App />, document.getElementById('root-main')) // 这里index.html里的root div最好换个名字(root-main),以免和子应用的冲突

4、路由配置中增加一个子应用的路由

这里不需要每个页面加一个路由
比如,子应用有 order/page1 和 order/page2 这两个路由,这里只需要添加一个childApp(这个是自己起名的),然后主应用配置菜单的时候,配置 childApp#/order/page1 和 childApp#/order/page2 这两个菜单就可以了

  ...
  // 子应用1
  {
    path: 'childApp',
    element: () => import('@/pages/childApp'),
  },
  ...

5、/pages/childApp/index.jsx

因为React不支持自定义事件,所以我们需要引入一个polyfill。
在<micro-app>标签所在的文件顶部添加polyfill,注释也要复制。

/** @jsxRuntime classic */
/** @jsx jsxCustomEvent */
import jsxCustomEvent from '@micro-zoe/micro-app/polyfill/jsx-custom-event' // 

function Index() {
  // name(必传):应用名称
  // url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
  // baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
  return (
    <div>
      <h1>子应用</h1>
      <micro-app name="app1" url="http://localhost:8052/" baseroute="/childApp"></micro-app>
    </div>
  )
}
export default Index

子应用

webpack.config devServer增加headers支持跨域

webpack.config.js

  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },

chainWebpack

  config.devServer.headers({
    'Access-Control-Allow-Origin': '*',
  })

publicPath

如果自动补全失败,可以采用运行时publicPath方案解决。

这是由webpack提供的功能,会在运行时动态设置webpack.publicPath

步骤1:  在子应用src目录下创建名称为public-path.js的文件,并添加如下内容

// __MICRO_APP_ENVIRONMENT__和__MICRO_APP_PUBLIC_PATH__是由micro-app注入的全局变量
if (window.__MICRO_APP_ENVIRONMENT__) {
  // eslint-disable-next-line
  __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}复制代码Error复制成功

步骤2:  在子应用入口文件的最顶部引入public-path.js

// entry
import './public-path'复制代码Error复制成功

子应用配置完毕。

数据通信

我这里主要是实现2种场景:
1、共享token和login的userInfo
2、子应用token失效要通知基座应用,清除token、userInfo并跳转登录页

更多通信方式:cangdu.org/micro-app/d…

1、共享token和login的userInfo

基座应用setGlobalData

import microApp from '@micro-zoe/micro-app'

...
microApp.setGlobalData({ userInfo: login.userInfo })
...

子应用获取数据

const globalData = window.microApp?.getGlobalData() // 返回全局数据
console.log('子应用拿到的 globalData', globalData);

2、子应用token失效的情况

子应用microApp.dispatch

// 子应用token失效的时候 触发:
function handleTokenFail() {
  console.log('子应用token 失效')
  window.microApp?.dispatch({ type: 'token失效' }) // 关键代码
}

基座应用监听

入口文件增加 microApp.addDataListener 代码

import ReactDOM from 'react-dom'
import App from './App'
import microApp from '@micro-zoe/micro-app'

microApp.start({
  // sockjs-node报错 要不然会一直刷新
  plugins: {
    modules: {
      app1: [
        {
          loader(code) {
            if (process.env.NODE_ENV === 'development' && code.indexOf('sockjs-node') > -1) {
              code = code.replace('window.location.port', 8052)
            }
            return code
          },
        },
      ],
    },
  },
})

function dataListener(data) {
  console.log('来自子应用my-app的数据', data)
}

/**
 * 绑定监听函数
 * appName: 应用名称
 * dataListener: 绑定函数
 * autoTrigger: 在初次绑定监听函数时如果有缓存数据,是否需要主动触发一次,默认为false
 */
microApp.addDataListener('app1', dataListener)

// // 解绑监听my-app子应用的函数
// microApp.removeDataListener(appName: string, dataListener: Function)

// // 清空所有监听appName子应用的函数
// microApp.clearDataListener(appName: string)

ReactDOM.render(<App />, document.getElementById('root-main'))

或者microApp.addDataListenermicroApp.removeDataListener也可以写在最外面的layout里

总结

其实大部分的用法和修改,官方文档都已经介绍的非常详细了,这里只是结合自己的项目的一些修改,可以看到micro-app相比qiankun对项目入侵非常少,而且涉及到的api很少且更容易理解。
后面会持续关注:)

未完成

1、子应用中如果使用了高德地图的scrip js会报错 —— 解决
2、因为之前使用history路由比较少,这里项目改成history可能会存在问题 —— 解决
3、部署到nginx,因为基座修改为了history路由,可能涉及到nginx的改动
4、处理子应用登录失效的情况 —— 解决

仓库地址

基座应用git:github.com/jiqishoubi/…