微前端乾坤js实践
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。解耦团队之间的龚和渡,解耦技术栈的龚和渡,节省团队间沟通成本,解决技术栈之间无法业务打通的能力
微前端架构具备以下几个核心价值:
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
1. 技术栈无关
* 主框架不限制接入应用的技术栈,微应用具备完全自主权
2. 独立开发、独立部署
* 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
3. 增量升级
- 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
4. 独立运行时
- 每个微应用之间状态隔离,运行时状态不共享
qiankunjs 特性
-
📦 基于 single-spa 封装,提供了更加开箱即用的 API。
-
📱 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
-
💪 HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
-
🛡 样式隔离,确保微应用之间样式互相不干扰。
-
🧳 JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
-
⚡️ 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
-
🔌 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。
React 接入乾坤微前端实践
Lerna
Lerna 是一个管理工具,用于管理包含多个软件包(package)的 JavaScript 项目
首先使用 npm
将 Lerna
安装到全局环境中
npm i lerna -g
创建一个新的 git
代码仓库
git init lerna-qiankun && cd lerna-qiankun
将上述仓库转变为一个 Lerna
仓库
lerna init
目录结构如下
lerna-qiankun/
packages/
package.json
lerna.json
进入 packages
文件目录创建项目 main-qiankun
, one-qiankun
, tow-qiankun
, three-qiankun
创建react 项目 create-react-app main-qiankun --template typescript
微前端主应用
-
进入项目
cd main-qiankun
-
释放配置文件打开
one-qiankun
webpack配置,执行命令:npm eject
, 释放后根目录会生成config
文件夹和script
文件夹 -
安装react路由
npm install react-router-dom
-
创建三个路由页面
/
,/one
,/*
, 代码如下:
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import './App.css';
import Home from './pages/home'
import One from './pages/one';
import ErrorPage from './pages/404';
function App() {
return (
<div className="App">
乾坤主应用
<BrowserRouter basename=''>
<Routes >
<Route path="/" element={<Home></Home>} ></Route>
<Route path="/one" element={<One></One>} ></Route>
<Route path="/*" element={<ErrorPage></ErrorPage>} ></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
-
执行命令
npm i qiankun -S
安装qiankun
-
在主应用中找到
one.tsx
添加qiankun
依赖及启动生命周期,代码如下:
import React, { useEffect, useMemo, useState } from 'react'
import { Outlet } from 'react-router-dom'
import { useSearchParams } from 'react-router-dom'
import { start } from 'qiankun'
const One = () => {
// 启动微前端调用子应用模块
useEffect(() => {
start()
}, [])
return (<>
<div className="app-box" >
ONE !!!
</div>
<div>
<div id="one">
<div>此处是子应用渲染区域</div>
</div>
</div>
</>
)
}
export default One;
- 配置完成
npm build
那到build
文件夹部署代码到nginx指向服务器
创建react 项目 create-react-app one-qiankun --template typescript
微前端主应用
-
进入项目
cd one-qiankun
-
释放配置文件打开
one-qiankun
webpack配置,执行命令:npm eject
, 释放后根目录会生成config
文件夹和script
文件夹, 找到script
文件夹下面的start.js
修改端口号3000
改成4000
-
安装react路由
npm install react-router-dom
-
创建三个路由页面
/dcw/
,/dcw/one
,/dcw/one-one
,/*
, 前缀/dcw
代码如下:
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import './App.css'
import Home from './views/home'
import One from './views/one'
import Two from './views/two'
import ErrorPage from './views/404'
function App() {
return (
<div className="App">
乾坤子应用
<BrowserRouter basename='/dcw'>
<Routes >
<Route path="/" element={<Home></Home>} ></Route>
<Route path="/one" element={<One></One>} ></Route>
<Route path="/one-one" element={<Two></Two>} ></Route>
<Route path="/*" element={<ErrorPage></ErrorPage>} ></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
- 配置子应用
one-qiankun
的入口逻辑index.tsx
代码如下:
import React from 'react'
import ReactDOM from 'react-dom/client'
// import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
console.log(window, 'window----')
// 此处是子应用独立访问渲染路由到 root div 标签下
if (!(window as any).__POWERED_BY_QIANKUN__) {
let root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
}
// 此处是微前端主应用调用执行生命周期
export async function bootstrap() {
console.log('bootstrap-----')
}
// 微前端挂载dome 生命周期
export async function mount(props: any) {
console.log('one start')
// ReactDOM.render(<App />, props.container ? props.container.querySelector('#one') : document.getElementById('root'));
let root = ReactDOM.createRoot(
document.getElementById('one') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
}
export async function unmount(props: any) {
console.log('unmount-----')
// ReactDOM.unmountComponentAtNode(
// props.container ? props.container.querySelector('#one') : document.getElementById('root'),
// );
}
export async function update(props: any) {
console.log('update-----')
console.log('update props', props)
}
- 配置子应用
webpack.config.js
// 打包出口处添加
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
- 配置完成
npm build
那到build
文件夹部署代码到nginx指向服务器
nginx 配置 主应用 与 子应用 代码如下:
server {
listen 8080;
server_name localhost;
charset utf-8;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#charset koi8-r;
#access_log logs/host.access.log main;
location /v2/ {
proxy_set_header Host 'dcw.com';
proxy_pass http://dcw.com;
proxy_read_timeout 10m;
}
location /boss/ {
proxy_set_header Host 'dcw.com';
proxy_pass http://dcw.com;
proxy_read_timeout 10m;
}
location / {
root /work/dist/build;
try_files $uri /index.html;
}
},
server {
listen 8081;
server_name localhost;
// 允许跨域
add_header 'Access-Control-Allow-Origin' *;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' *;
add_header 'Access-Control-Allow-Headers' *;
location / {
root /u/work/dist/oneqiankun/build;
try_files $uri /index.html;
}
}
- 访问
localhost:8080
即可在主应用看到localhost:8081
子应用的界面,如下图: