微前端最近是个很火的概念,社区其实也有很多成熟的方案去实现。做为一个umi
的重度使用者,就必须来体验一下阿里基于single-spa
开发的qiankun
了,而umi跟qiankun的最佳结合方案,无疑是qiankun为umi量身订造的插件@umijg/plugin-qiankun
。那咱们就跟着官网的文档开造吧。html
npx @umijs/create-umi-app
npm install
npm install @umijs/plugin-qiankun --save
npm start
共建立3个项目,一个做为主应用,2个做为子应用前端
export default {
qiankun: {
master: {
// 注册子应用信息
apps: [
{
name: 'app1', // 惟一 id,须要与微应用中的package.json中的name一致
entry: '//localhost:8001', // html entry
},
{
name: 'app2', // 惟一 id,须要与微应用中的package.json中的name一致
entry: '//localhost:8002', // html entry
}
],
},
},
};
复制代码
export default {
routes: [
{
path: '/',
component: '../layouts/index.js',
routes: [
{
path: '/app1',
microApp: 'app1',
// 配置Loading属性
microAppProps: {
autoSetLoading: true,
className: 'myContainer',
wrapperClassName: 'myWrapper',
}
},
{
// app2
// ...
}
],
},
],
}
复制代码
package.json
中配置"name": "app1"
。同时也须要配置.umirc.ts文件开启qiankun。export default {
qiankun: {
slave: {}
}
}
复制代码
.env
中:PORT=8001
HMR=none
复制代码
若是遇到了由于dev scripts端口号等问题产生冲突以至于报错,能够先关闭HMR试试react
src/app.ts
中导出一个对象qiankun
export const qiankun = {
// 应用加载以前
async bootstrap(props) {
console.log('app1 bootstrap', props);
},
// 应用 render 以前触发
async mount(props) {
console.log('app1 mount', props);
},
// 应用卸载以后触发
async unmount(props) {
console.log('app1 unmount', props);
},
};
复制代码
如今运行3个项目,就能经过主应用来访问子应用了,例如访问http://localhost:8000/app1
显示的是子应用的页面。这里须要注意的是,子应用的端口号是_8001_,那么原来的子应用发出的处于同一域名下的后端请求也是在_8001_端口上,但经过主应用来访问,后端请求就会变为_8000_端口,须要作一个代理转发,能够在.umirc.ts
中配置proxy
。npm
这时候应用之间是基本独立的,咱们还须要加入供应用间通信的全局状态json
这里主要使用@umijs/plugin-model
的useModel
来将数据全局化。umi应该已经内置了@umijs/preset-react
插件集,其中就包括了@umijs/plugin-model
bootstrap
在src/app.ts
中导出一个函数useQiankunStateForSlave
,这个函数返回的内容会注入到props中传递给子应用。这里有个坑,useState
返回的setXXX方法不能命名为setGlobalState
,否则会在mount阶段被qiankun的另外一个同名函数覆盖。后端
// src/app.ts
export function useQiankunStateForSlave() {
const [globalState, setQiankunGlobalState] = useState({ str: 'aaa'})
return {
globalState,
setQiankunGlobalState
};
}
复制代码
若是主应用也须要用到这个globalState
的话,也能够在组件中借助useModel
访问@@qiankunStateForSlave
:markdown
const { globalState } = useModel('@@qiankunStateForSlave');
app
子应用经过useModel
访问@@qiankunStateFromMaster
这个model以获取主应用传过来的propsasync
export default () => {
const masterProps = useModel('@@qiankunStateFromMaster');
useEffect(() => {
masterProps.setQiankunGlobalState({ str: 'bbb' })
}, [])
return (
<p>{JSON.stringify(masterProps.globalState)}</p>
);
}
复制代码
这样,主应用负责管理globalState,子应用也能取到globalState并调用masterProps上的方法来通知主应用修改globalState
子应用app2将主应用的globalState从aaa改为bbb
每一个中后台系统,基本都是须要获取用户权限来限制某些页面的访问权。那么在微前端这种使用场景,最好是由主应用来统一管理每一个子应用的用户权限等信息。最好还能不改变原来单独访问子应用时的逻辑。
假设token放在localStorage中,这里想到了2个方案:
子应用经过当前域名识别出是否位于主应用,是则不走本身的登陆校验逻辑。
本人也尝试接入umi2版本的项目,即按照对应版本的qiankun插件
npm install @umijs/plugin-qiankun@umi2 --save
也能运行起来
可是这个版本的qiankun插件,子应用并不能使用@@qiankunStateForSlave
来获取masterProps,这个版本是使用const masterProps = useRootExports()
来获取masterProps。显然这样的话主应用还得额外处理供umi2子应用使用的全局state,感受不是很方便。