背景
最近我们公司有个”巨石“项目想要改造一下,但是由于项目开始时间久远,经历过无数人的手,乱七八糟的,用的技术栈都很旧了,都没有更新,antd
的版本都是 3.X
的,之前也没有人去升级,继续开发感觉就是在 ”堆垃圾“ 一样,要重构又要去了解很多以前的业务逻辑,产品经理都换了好几个,之前的业务逻辑现在的产品也不是很清楚,不太好重构。所以就萌生了要不用微前端的方法,把新的模块给做到一个新的子应用里,当然也只是在尝试阶段。了解了一些微前端的解决方案以后,内部还是觉得用 qiankunjs
这个解决方案。这里就不比较解决方案之间的优缺点了,就是在做这个微前端的过程中,感觉雷好多,今天就总结一下父子应用间的通信的实现。我还有尝试过 vite
,但是配置起来也挺麻烦的,最后还是放弃了。用个 demo 来记录一下这个摸索的过程
技术栈:主应用(Vue3
)+ 子应用(React18
) + 子应用(Vue2
)
代码实现
- 主应用注册
registerApp.js
这里的
props
非常关键,一定要记得传,如果忘记了,你可以得一层一层往上排了
import { registerMicroApps, start } from 'qiankun'
import actions from './shared'
registerMicroApps([
{
name: 'reactApp',
entry: 'http://localhost:7100/',
container: '#container',
activeRule: '/react',
// 这个 props 很关键,是子应用能不能访问到全局状态的点
props: { actions },
},
{
name: 'vueApp',
entry: 'http://localhost:7200/',
container: '#container',
activeRule: '/vue',
props: { actions },
},
])
start({
sandbox: {
// experimentalStyleIsolation: true,
strictStyleIsolation: true,
},
})
- 状态管理
shared/index.js
这里就简单的使用了一个对象作为状态管理,项目上可以使用
vuex
或者redux
等状态管理工具来出来,我想用最简单的方式来表达,不想牵扯太多其他的技术栈,代码仅为记录摸索qiankunjs
的过程
import { initGlobalState } from 'qiankun'
const initialState = {
count: 0,
}
const actions = initGlobalState(initialState)
// 例子里只有 count 一个key,如果对象比较复杂建议使用状态管理工具进行管理
// 这代码看着很呆
actions.getCount = () => initialState.count
actions.onGlobalStateChange(state => {
// 这里得修改 initialState 我是有点不愿意的
// 但是没有用状态管理,应该只能这么操作了
initialState.count = state.count
})
export default actions
- 子应用(
React18
)index.js
这个
props
要在他的生命周期函数里才有,要记得传,如果忘记传了就啥也拿不到了
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
function render(props) {
const { container } = props
const eleDom = container
? container.querySelector('#root')
: document.getElementById('root')
const root = ReactDOM.createRoot(eleDom)
// 这里也很重要,一定要通过 props 传给子应用,不然应用里啥也拿不到
root.render(<App data={{ ...props }} />)
}
export async function bootstrap() {}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
render(props)
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount({ container }) {
// 这里的 id 得看好了,写错了就老报错,很烦的
const eleDom = container
? container.querySelector('#root')
: document.getElementById('root')
// react18的组件卸载方法和以前的有点不一样,如果使用18的小伙伴得注意一下咯
// ReactDOM.unmountComponentAtNode(eleDom);
const root = ReactDOM.createRoot(eleDom)
root.unmount()
}
if (!window.__POWERED_BY_QIANKUN__) {
render({})
}
- 修改全局状态
App.js
从
props
获取到的count
值是不会触发页面的更新的,所以最好还是重新存到子应用中的state
中吧
import { useState } from 'react'
import logo from './logo.svg'
import './App.css'
function App({ data }) {
// 这样进来的时候,还能保持 count 的指,切换应用也可以保持
const [count, setCount] = useState(data.actions.getCount())
// 监听状态的变化,重新存起来
// 如果想在页面上显示,还是重新存起来好
data.onGlobalStateChange((state) => {
setCount(state.count)
})
// 修改状态
const addCountHandler = () => {
data.setGlobalState({ count: data.actions.getCount() + 1 })
}
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<button onClick={addCountHandler}>add count</button>
<h2>{count}</h2>
{/* 如果你想这么做,对不起,页面可不会更新 */}
{/* <h2>{data.actions.getCount()}</h2> */}
</header>
</div>
)
}
export default App
总结
我也在网上找了很多资料,qiankunjs
的文档总觉得有点简陋
就这样!!!好像说了怎么做,又好像没说,有一些细节还是得自己实践过才知道。如果有想入手的小伙伴,可以自己动手尝试一下,如果你不是很熟悉的话,不要用 vite
,很打击信心,很会干扰你学习的进度。