qiankun学习记录
微前端是什么qiankun官网是这么介绍的
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 微前端架构具备以下几个核心价值:
-
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 -
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 -
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
-
独立运行时
每个微应用之间状态隔离,运行时状态不共享 -
通俗的讲就是将不同框架所产生的项目进行一个整合,使得在访问时可通过一个主应用进入不同的子应用,子应用可以是vue/react/angular
开始
首先我们比如说有三个项目他们如下
base作为主应用用的vue3 两个子应用为react项目与vue2项目
- 首先在应用中下载qiankun
$ yarn add qiankun # 或者 npm i qiankun -S
应用配置
主应用配置
主应用main.js中加入如下配置
/* eslint-disable */
import { createApp } from 'vue'
import App from './App.vue'
//引入 qiankun
import { registerMicroApps, start } from "qiankun"
// import "./registerApp" 也可以写个文件引入
import { createRouter, createWebHistory } from "vue-router"
import route from "@/route/index"
const router = createRouter({
history: createWebHistory(),
routes: route
})
createApp(App).use(router).mount('#app')
//qiankun 配置
registerMicroApps([//注册子应用
{
name: 'vue2-app',
entry: '//localhost:8081',//注意路径端口 如果路由模式带#记得带#
container: '#container',//此为为子应用提供的容器
activeRule: '/app-vue2',//子应用路由
},
{
name: 'react-app',//同上
entry: '//localhost:8082',
container: '#container',
activeRule: '/app-react',
}
], {//此处为子应用的生命周期
beforeLoad: (app) => console.log('应用加载', app.name),
beforeMount: (app) => console.log('挂载前', app.name),
afterMount: (app) => console.log('挂载后', app.name),
beforeUnmount: (app) => console.log('卸载前', app.name),
afterUnmount: (app) => console.log('卸载后', app.name),
})
//启动
start();
在vue.config.js文件中进行如下配置
devServer: {
//headers 设置可跨域
headers: {
'Access-Control-Allow-Origin': '*', // 设置允许跨域请求,否则会因为在其他端口号获取资源报错
},
port: 8080, // 设置每次打开本地的端口号
},
现在我们的主应用配置就已经完了 在页面中可以使用vue-router方式进行跳转
<div>
<!--此处路由为main.js中 activeRule的配置-->
<router-link to="/app-vue2">app-vue2</router-link>
</div>
<div>
<router-link to="/app-react">app-react</router-link>
</div>
<div id="container"></div>
子应用vue2-app配置
src下新建public-path.js文件
/* eslint-disable */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
main.js修改如下
import './public-path';
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
Vue.use(VueRouter) //qiankun官网的示例 <router-link/>不生效我这里直接初始化下
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
//这里判断的是是否是从主应用进入如果是则带入/app-vue2/前缀
//不然路由则不在子应用下,会再次访问到主应用那里
base: window.__POWERED_BY_QIANKUN__ ? '/app-vue2/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
//若无container则说明是单独访问非从主应用进入
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
//协议
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
//此处会传入props内部带有渲染的容器也就是主应用的#container
//使用render函数创建应用方便子应用卸载时销毁,不然占位不拉屎
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
//销毁
}
vue.config.js更改如下
const { name } = require('./package');
module.exports = {
devServer: {
port:8081,
//设置可跨域
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式 默认是systemjs
// jsonpFunction: `webpackJsonp_${name}`,
},
},
};
子应用 react-app 配置
官网使用的插件是 @rescripts/cli 我这里使用的是 react-app-rewired
yarn add react-app-rewired -D
下载完之后更改package.json文件运行配置
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
}
记得改端口号我这里是8082 .env文件 PORT=8082
最外层新增config-overrides.js文件
const { name } = require('./package.json')
console.log(name)
module.exports = {
webpack: function (config, env) {
if (Array.isArray(config.entry)) {
config.entry = config.entry.filter(
(e) => !e.includes('webpackHotDevClient')
)
}
// config.entry = config.entry.includes('webpackHotDevClient')
config.output.library = `${name}-[name]`
config.output.libraryTarget = 'umd'
config.output.chunkLoadingGlobal = `webpackJsonp_${name}`
// 写项目启动的源,否则图片无法显示
config.output.publicPath = '//localhost:8082/'
return config
},
devServer: (configFunction) => {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost)
config.open = false
config.hot = false
//设置可跨域
config.headers = {
'Access-Control-Allow-Origin': '*',
}
// Return your customised Webpack Development Server config.
return config
}
},
}
src内新增文件public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
修改index.js文件
import './public-path';
import React from 'react';
import App from './App';
//import ReactDOM from 'react-dom'; 16版本
import ReactDOM from 'react-dom/client';//18版本
//这里官网给的是16,我当前下的是18
let root = null
function render(props) {
const { container } = props;
//18 版本 ReactDOM.render弃用了改为 createRoot
root = ReactDOM.createRoot(container ? container.querySelector('#root') : document.querySelector('#root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
//16 版本
// ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('[react16] react app bootstraped');
}
export async function mount(props) {
console.log('[react16] props from main framework', props);
render(props);
}
export async function unmount(props) {
const { container } = props;
root.unmount(container ? container.querySelector('#root') : document.querySelector('#root'));//18版本
// ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));16版本
}
然后运行三个项目
主应用
子应用vue2-app
子应用react-app
单独访问
vue2-app
react-app