微前端-乾坤框架 -> 你🐘弟,我又来了今天咱们基于乾坤+react来玩个微前端
乾坤是啥🤔️
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
目前 qiankun 已在蚂蚁内部服务了超过 2000+ 线上应用,在易用性及完备性上,绝对是值得信赖的。
既然他这么牛🍺,那作为一个奋斗在一线的前端程序猿这个技能肯定是要🫴一下
啥是微前端🤔️
概述:
简单的来说微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
优点:
- 技术站无关(不限制应用的技术站)
- 独立开发独立部署
- 增量升级
- 独立运行
这边就列举了一些,官网又详细的介绍 乾坤官网
运用与实现
下代码块中menu🌝
export const menu = [
{
name: 'myApp1', // app name registered
entry: '//localhost:3001',
container: '#myApp1',
activeRule: '/app1',
props: {
name: 'fs',
age: 01
}
},
{
name: 'myApp2',
entry: '//localhost:3002',
container: '#myApp2',
activeRule: '/app2',
},
];
1.创建项目安装乾坤
a.这边随便你创建几个应用,但是需要拿出来一个座位基座来使用
npx create-react-app my-app --template typescript
可能这边大家心里会有个小疑问,如果我造了多个应用,那么端口号怎么办???别慌👌拿捏
在你项目中创建一个.env文件 文件内容PORT=3000以此类推给上不同的端口号即可
PORT=3000
b.安装乾坤插件
yarn add qiankun
2.设置主应用
在你的基座(主应用)入口文件中注册子应用(registerMicroApp([{}]))
格式:
//当匹配到activeRule的时候,他会获取entry资源,渲染到container
{
name: 'myApp1', // app name
entry: '//localhost:3001', // 子应用注册的端口号
container: '#myApp1', // 渲染到哪里 (这边是个选择器)从DOM元素中获取id为#yourContainer元素,做为容器
activeRule: '/app1', //路由匹配规则
props:{
name: '小冯',
age:13,
}
}
// 以上就是注册子应用的格式,子应用可以接收到以上声明的所有状态
在基座入口文件中加入乾坤api注册子应用
//代码中实现是这个样子的(我们以react create app 为例)
import './public-path.js';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { registerMicroApps, start } from 'qiankun'; // 倒入乾坤框架的两个api
import { menu } from './router'; // 这个menu是自己生命的一个数组,格式类型[{}]
registerMicroApps(menu); // 注册子应用
start(); //启动微应用的api
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<div>
<App />
</div>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
3.基座中需要创建子应用的入口
我这边是放在了APP.tsx文件中
import React from 'react';
import './App.css';
import { menu } from './router'; // 在此我将子应用的配置项单独写了一条数据提了出去
function App() {
const BASECLASS = 'FS-';
return (
<div className="App">
<div className="box">
<div className={BASECLASS + 'header'}></div>
<div className={BASECLASS + 'appContent'}>
<div className={BASECLASS + 'sidebar'}>
{menu.map(item => (
<a key={item.name} href={item.activeRule} className={BASECLASS + 'a'}>
to {item.name}
</a>
))}
</div>
<div className={BASECLASS + 'appBox'}>
{menu.map(item => (
<div key={item.name} id={item.name} className={BASECLASS + 'myApp'}></div>
))}
</div>
</div>
</div>
</div>
);
}
export default App;
App 的 css
.App {
text-align: center;
width: 100vw;
font-size: 12px;
overflow: auto;
}
.box {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
}
.FS-header {
min-height: 40px;
width: 100vw;
background-color:#3e3f4c;
}
.FS-appContent {
position: relative;
}
.FS-sidebar {
position: absolute;
height: 100%;
width: 18%;
background-color:#3e3f4c;
}
.FS-a {
display: block;
width: 100%;
height: 40px;
font-size: 18px;
line-height: 40px;
border-radius: 8px;
text-decoration: none;
background: #be98aa;
border-bottom: solid rgb(186, 182, 182);
}
.FS-a:last-child {
border-bottom: none;
}
.FS-appBox {
position: relative;
width: 82%;
left: 18%;
overflow: hidden;
}
4.子应用接入主应用
在子应用中写入乾坤的生命周期
import './public-path.js';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
export async function bootstrap() {
console.log('[react] react app bootstraped');
}
// @ts-ignore
export async function mount(props) {
console.log(props);
ReactDOM.render(
<App />,
props.container ? props.container.querySelector('#root') : document.getElementById('root')
);
}
// @ts-ignore
export async function update(props) {
console.log('update props', props);
}
// @ts-ignore
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(
props.container ? props.container.querySelector('#root') : document.getElementById('root')
);
}
// 判断是否在乾坤的运行环境下
// @ts-ignore
if (!window.__POWERED_BY_QIANKUN__) {
ReactDOM.render(<App />, document.getElementById('root'));
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
5.解决跨域问题
写到这边发现启动存在跨域上的问题,怎么办!!☃️(雪上加霜)
处理方案
问题不大,既然是夸应用的,那必然要解决这个跨域的问题,官网也有相应的处理方法,我这边利用了react-app-rewired,重写了webpack打包的逻辑(也不算重写,只是覆盖)
让我们先安装一下yarn add react-app-rewired -D
创建一个文件名为:config-overrides.js 将此文件放到项目的最外层,和package.json一个层级
const { name } = require('./package');
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = 'umd'; // 这一行很重要,一定要是umd格式的为啥可以看一下官网
config.output.jsonpFunction = `webpackJsonp_${name}`;
config.output.globalObject = 'window';
return config;
},
// 以下是对服务的配置,解决跨域问题。(当然你也可以不这么配置,看个人所需)
devServer: (_) => {
const config = _;
config.headers = {
'Access-Control-Allow-Origin': '*',
};
config.historyApiFallback = true;
config.hot = false;
config.watchContentBase = false;
config.liveReload = false;
return config;
},
};
配置玩之后我们需要对脚本进行一下修改
"scripts": {
"start": "react-app-rewired start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
这个时 start一下 发生以下的场景,哎~我的图片资源呢😩
问题不大我们只需要创建一个文件 - 文件名public-path.js 放在src目录下和入口文件一个层级,然后在子应用的index文件中第一行引入这个js文件即可。
/* eslint-disable */
//这边判定运行环境是不是乾坤,如果是的话就修改当前环境
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
轻松搞定🥰
简简单单的搭建一个最基础的应用凑活用!😝
下次更新,让我们来自己手撕一个乾坤🫠