前言
随着微服务的兴起,后端在这一方面有了很好的解决方案。前端目前流行的就是使用qiankun去构建微前端架构。
什么是qiankun
Qiankun:基于Single-Spa,阿里系开源微前端框架。具有以下特点:
简单——任意 js 框架均可使用。微应用接入像使用接入一个 iframe 系统一样简单,但实际不是 iframe。
完备——几乎包含所有构建微前端系统时所需要的基本能力,如 样式隔离、js 沙箱、预加载等。
生产可用——已在蚂蚁内外经受过足够大量的线上系统的考验及打磨,健壮性值得信赖。
构建基础应用
今天我们将会使用Vue项目作为基座,来联合Vue和React的两个子应用来构建一个微前端架构体系。为方便大家理解,下面我整理了架构图:
需要构建的三个应用分别是Vue主应用(qk-base)、Vue子应用(qk-vue)、React子应用(qk-react)。
// Vue主应用(基座)
vue create qk-base
// Vue子应用
vue create qk-vue
// react子应用
npx create react-app qk-react
创建完成后文件夹中就会有以下三个项目:
主应用(基座)配置
菜单和样式配置
为更方便展示应用的切换我们对主应用的首页进行配置,使用element-ui的菜单对其进行优化展示。使其可以通过切换来显示不同的应用信息。
安装elementUi
npm i element-ui -S
// main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
App.vue
<el-menu :router="true" class="el-menu-demo" mode="horizontal" >
<el-menu-item index="/">主应用页面</el-menu-item>
<el-menu-item index="/vue">Vue应用</el-menu-item>
<el-menu-item index="/react">react应用</el-menu-item>
</el-menu>
<router-view></router-view>
<!-- 加载VUE子应用 -->
<div id="vue"></div>
<!-- 加载React子应用 -->
<div id="react"></div>
在这里,我们设定Vue子引用的路径开头为'/vue',React子应用的开头为'/react'。并使用id="vue"和id="react"两个div来接收他们。
值得一提的是,主应用的路由也可以通过router-view来进行路由切换。
主应用qiankun配置
安装qiankun
yarn add qiankun # 或者 npm i qiankun -S
引入qiankun
main.js
import { registerMicroApps, start } from 'qiankun';
// ...
let apps = [
{
name: 'vue faxian', // 子应用名称
entry: '//localhost:10000', // 默认会加载这个html解析里面的js动态的执行,因为请求了资源子应用需要支持跨域
container: '#vue', // 挂在到那个节点上
activeRule: '/vue', // 访问某个URL的时候将这个端口号挂在到这个上去
},
{
name: 'react exam',
entry: '//localhost:20000',
container: '#react',
activeRule: '/react',
},
];
registerMicroApps(apps); // 注册应用
start({
prefetch: false // 取消预加载
}); // 开启
我们分别使用 10000和 20000端口去分别监听Vue和React两个子应用。
子应用Vue配置
在子应用中必须要三个函数(必须是promise的):bootstrap()、mount()、unmount()。我们这里使用async函数将其导出。
main.js
// 子组件协议
/**
* 启动时
*
*/
export async function bootstrap(){}
/**
* 创建时
* @param {*} props 父应用传过来的数据
*/
export async function mount(props){
render(props)
}
/**
* 卸载时
*/
export async function unmount(){
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
}
我们需要更新子应用路由的一些配置,包括设置根目录和调为history模式:
router/index.js
// your router config ...
const router = new VueRouter({
mode: 'history', // 设置路由模式为history
base:'/vue', // 设置根目录为 /vue
routes
})
export default router;
设置创建vue实例的方法 render(),这里是挂载到自己的html中,基座会拿到这个挂载后的html将其插入进去。
main.js
function render(){
instance = new Vue({
router,
// store // 也可以传递父应用的vuex
render: h => h(App),
}).$mount('#app') // 这里是挂载到自己的html中,.基座会拿到这个挂载后的html将其插入进去
}
当然我们需要独立运行时也能正常运行,qiankun提供一个参数__POWERED_BY_QIANKUN__来判断是不是在独立运行:
main.js
// 使用 webpack 运行时 publicPath 配置
if(window.__POWERED_BY_QIANKUN__){ // 动态添加public_path
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 独立运行微应用
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
render();
}
最后我们配置上打包就可以了。
vue.config.js
module.exports = {
devServer:{
port:10000, // 启用10000端口来监听
headers:{
'Access-Control-Allow-origin':'*' // 配置本地运行时允许跨域
}
},
configureWebpack:{
output:{
library: 'vueApp',
libraryTarget: 'umd'
}
}
}
运行后的结果:
子应用React配置
想要配置React项目,我们首先要重写配置文件。这边我们借助react-app-rewired将我们的React配置进行变线(注意是把线路改变不是变现)。
yarn add react-app-rewired
更改我们的启动项,将原来的react-scripts改为react-app-rewired:
package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
}
在根目录中建立配置文件config-overrides.js
config-overrides.js
module.exports = {
webpack:(config)=> {
config.output.library = 'reactApp'; // 打包的类库,项目名称
config.output.libraryTarget = 'umd';
config.output.publicPath = 'http://localhost:20000/'; // 端口
return config;
},
devServer:(configFunction)=>{
return function(proxy,allowedHost){
const config = configFunction(proxy,allowedHost);
config.headers = {
'Access-Control-Allow-origin':'*' // 配置都允许跨域
}
return config;
}
}
}
建立配置文件.env配置完成端口号
.env
PORT = 20000
WDS_SOCKET_PORT= 20000
和上面的项目一样我们需要导出固定名称的三个方法:bootstrap()、mount()、unmount()。
src/index.js
// 子组件协议
/**
* 启动时
*
*/
export async function bootstrap(){}
/**
* 创建时
* @param {*} props
*/
export async function mount(props){
render(props)
}
/**
* 卸载时
* @param {*} props
*/
export async function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
}
封装一个render(),本地运行时候进行判断,使其可以独立运行。
src/index.js
function render(){
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
}
// 独立运行微应用
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
render();
}
然后我们配置ReactRouter
安装router
yarn add react-router-dom
这边简单的配置router和页面
function App() {
return (
<BrowserRouter basename='/react'>
<Link to='/'>首页</Link>
<Link to='/about'>关于页面</Link>
<Route path='/' exact render={() => <div className="App">首页</div>}></Route>
<Route path='/about' exact render={() => <div className="App">关于页面</div>}></Route>
</BrowserRouter>
);
}
export default App;
yarn start 后就发现开始运行读取我们自己的配置文件了
最终效果:
数据通信
比方说我们现在需要将主应用的用户信息同步到子应用中。
我们可以通过配置主应用的main.js来传递参数,当然这里可以以是vuex或者redux
在子应用的mount方法中去接收这个参数
qk-vue main.js
export async function mount(props){
if(props?.userInfo){
console.log(props.userInfo)
// 设置主应用过来的参数
window.sessionStorage.setItem('userInfo',props.userInfo);
}
render(props)
}