qiankun 是目前较成熟可以开箱即用的实现微前端的框架。它主要特点有:
- 简单 - 任意 js 框架均可使用。微应用接入像使用接入一个 iframe 系统一样简单,但实际不是 iframe。
- 完备 - 几乎包含所有构建微前端系统时所需要的基本能力,如 样式隔离、js 沙箱、预加载等。
- 生产可用 - 已在蚂蚁内外经受过足够大量的线上系统的考验及打磨,健壮性值得信赖。
qiankun2.0 提供了两种接入微应用的方式:基于路由配置、手动加载微应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import React from 'react'; import ReactDOM, { render } from 'react-dom'; import { start, registerMicroApps, setDefaultMountApp } from 'qiankun'; import App from './App';
render(<App></App>, document.getElementById('mainApp'));
registerMicroApps([ { name: 'react-app', entry: '//localhost:8888', container: '#reactApp', activeRule: '/react-app', }, { name: 'vue-app', entry: '//localhost:8080', container: '#vueApp', activeRule: '/vue-app', }, { name: 'ng-app', entry: '//localhost:4200', container: '#ngApp', activeRule: '/ng-app', }, ]);
setDefaultMountApp('/ng-app');
start();
|
适用于需要手动 加载/卸载 一个微应用的场景。
通常这种场景下微应用是一个不带路由的可独立运行的业务组件。 微应用不宜拆分过细,建议按照业务域来做拆分。业务关联紧密的功能单元应该做成一个微应用,反之关联不紧密的可以考虑拆分成多个微应用。 一个判断业务关联是否紧密的标准:看这个微应用与其他微应用是否有频繁的通信需求。如果有可能说明这两个微应用本身就是服务于同一个业务场景,合并成一个微应用可能会更合适。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import React from 'react'; import ReactDOM, { render } from 'react-dom'; import { loadMicroApp, initGlobalState, MicroAppStateActions } from 'qiankun'; import App from './App';
render(<App></App>, document.getElementById('mainApp'));
const actions: MicroAppStateActions = initGlobalState({ 'vue-app': [], 'react-app': [], 'ng-app': [], });
loadMicroApp({ name: 'vue-app', entry: '//localhost:8080', container: '#vueApp', }); loadMicroApp({ name: 'react-app', entry: '//localhost:8888', container: '#reactApp', }); loadMicroApp({ name: 'ng-app', entry: '//localhost:4200', container: '#ngApp', });
actions.onGlobalStateChange((state, prev) => { });
|
qiankun 的接入使用并不复杂,但它对需要接入的微应用打包配置提了一些要求,并且会一些坑。下面列的都是基于 webpack5 的配置方式。
public-path.js
在每个微应用工程下都需要建一个 public-path.js 文件,内容如下:
1 2 3 4 5
| if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }
|
public-path.js
应该被引入工程启动文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import './public-path'; import { createApp } from 'vue'; import { store } from './store'; import App from './App.vue';
let instance: any = null; function render(props: any) { const { container } = props; instance = createApp(App); instance.use(store); instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange; instance.config.globalProperties.$setGlobalState = props.setGlobalState; instance.mount(container ? container.querySelector('#vueApp') : '#vueApp'); }
if (!window.__POWERED_BY_QIANKUN__) { render({}); }
export async function bootstrap() { console.log('%c ', 'color: green;', 'vue3.0 app bootstraped'); }
export async function mount(props: any) { render(props); }
export async function unmount() { instance.unmount(); instance._container.innerHTML = ''; instance = null; }
|
webpack 配置
对于用 webpack 构建的工程有几个地方需要注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| output: { path: path.resolve(__dirname, './dist'), library: `${name}`, libraryTarget: 'umd', chunkLoadingGlobal: `webpackJsonp_${name}`, globalObject: 'window', }
devServer: { hot: true, historyApiFallback: true, compress: true, open: true, port: 8080, injectClient: false, headers: { 'Access-Control-Allow-Origin': '*', }, }
|
注意 ⚠️:如果是 webpack5
则 "webpack-dev-server": "^4.0.0-beta.0"
需要用这个版本。
qiankun 提供了在主应用和微应用之间数据同步的方案: initGlobalState(state)
初始化
在主应用初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import React from 'react'; import ReactDOM, { render } from 'react-dom'; import { loadMicroApp, initGlobalState, MicroAppStateActions } from 'qiankun'; import App from './App';
render(<App></App>, document.getElementById('mainApp'));
const actions: MicroAppStateActions = initGlobalState({ 'vue-app': [], 'react-app': [], 'ng-app': [], });
loadMicroApp({ name: 'vue-app', entry: '//localhost:8080', container: '#vueApp', }); loadMicroApp({ name: 'react-app', entry: '//localhost:8888', container: '#reactApp', }); loadMicroApp({ name: 'ng-app', entry: '//localhost:4200', container: '#ngApp', });
actions.onGlobalStateChange((state, prev) => { });
|
微应用监听
在主应用初始化 GlobalState
之后,我们可以在微应用获取到获取三个方法用来处理 State:
- onGlobalStateChange:
(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void
, 在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback
- setGlobalState:
(state: Record<string, any>) => boolean
, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
- offGlobalStateChange:
() => boolean
,移除当前应用的状态监听,微应用 umount 时会默认调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import './public-path'; import { createApp } from 'vue'; import { store } from './store'; import App from './App.vue';
let instance: any = null; function render(props: any) { const { container } = props; instance = createApp(App); instance.use(store); instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange; instance.config.globalProperties.$setGlobalState = props.setGlobalState; instance.mount(container ? container.querySelector('#vueApp') : '#vueApp'); }
if (!window.__POWERED_BY_QIANKUN__) { render({}); }
export async function bootstrap() { console.log('%c ', 'color: green;', 'vue3.0 app bootstraped'); }
export async function mount(props: any) { render(props); }
export async function unmount() { instance.unmount(); instance._container.innerHTML = ''; instance = null; }
|
在微应用拿到onGlobalStateChange
、setGlobalState
、offGlobalStateChange
方法后就可以根据自己的业务需要愉快的和其它应用交流了。但是如果你发现有两个微应用数据交流过多,则你需要考虑将这个两个微应用合并了。
代码
Refer To
https://qiankun.umijs.org/zh/guide
https://juejin.cn/post/6844904151231496200
https://juejin.cn/post/6846687602439897101