-
微前端的好处
①团队自治,独立开发和部署
②技术灵活,可兼容老项目
③业务颗粒化
-
目前微前端的方案
- iframe
子应用可以通过iframe标签嵌入到父应用中,iframe具有天然隔离的属性,各个子应用之间及子应用和父应用之间可以做到互不影响。
缺点:①刷新页面,iframe中的页面的路由会丢失
②全局上下文完全隔离,内存变量不共享
③弹框的遮罩不能覆盖整个浏览器
④慢,每次子应用进入都是一次浏览器上下文重建,资源重新加载的过程 - single-spa
最早的微前端框架,也可以兼容很多技术。
在基座中注册所有子应用的路由,当url改变时进行匹配,匹配到哪个子应用就会去加载对应的哪个子应用。
相对于iframe的框架,不存在刷新,路由丢失问题
缺点:①没有实现js和css隔离
②需要修改大量的配置,不能开箱即用 - qiankun
基于single-spa封装,提供了开箱即用的API
HTML Entry的方式接入,像使用iframe一样简单
实现了single-spa不具备的样式隔离和js隔离
资源预加载,在浏览器空闲时间预加载未打开的微前端应用,加速微应用打开速度。
-
qiankun介绍
- 基座
第一步:安装
首先npm i qiankun第二步:修改入口文件
在入口应用处添加
import {start,registerMicroApps}from 'qiankun'
//要添加的子应用列表
const apps =[
{
name:'sub-react'//子应用名称
entry:'//localhost:8080',//加载这个路径下的html,解析里面的js
activeRule:'/sub-react',//匹配的路由
container:'sub-app'//加载的容器(id的值)
}
]
//注册子应用
registerMicroApps(apps,{
beforeLoad: [async app => console.log('before Load',app.name)]
beforeMount:[async app => console.log('before Mount',app.name)],
afterMount:[async app => console.log('After Mount',app.name)]
})
//启动
start()
//子应用渲染区
- 子应用
react子应用
其中使用到react-app-rewired工具来改造webpack入口文件改造
import '../public-path.js'
let root = null
function render(props){
const {container}=props
const dom =container?container.querySelector('#root'):document.getElementById('root')
root = createRoot(doom)
root.render(
)
}
//判断是否在qiankun的环境下
if(!window._POWERED_BY_QIANKUN_){
render({})
}
//生命周期
//bootstrap只会在微前端应用初始化的时候调用一次
export async function bootstrap(){
}
//应用每次进入都会调用mount方法
export async function mount(props){
render(props)
}
//应用每次切出/卸载调用
export async function unmount(){
root.unmount()
}
新增public-path.js文件(需要在入口文件引入)
publicPath
默认值: 空字符串。publicPath是非常有必要配置的,他是项目中引入静态资源(js、css)时的基础路径。
例如:outPut.publicPath = ‘/dist/’;
if(window._POWERED_BY_QIANKUN_){
//动态设置webpack publicPath,防止资源加载出错
_webpack_public_path_ =window._INJECTED_PUBLIC_BY_QIANKUN_
}
修改webpack配置使用react-app-rewired
npm i react-app-rewired
在根目录下新增config-overrides.js文件
const {name} = require('./package')
module.exports = {
webpack:(config) => {
config.output.library =`${name}-[name]`
config.output.libraryTarget ='umd'
config.output.chunkLoadingGlobal = `webpackJson_${name}`
return config
}
}
vue子应用
vue3+vite
- 安装:npm i vite-plugin-qiankun
- 修改vite.config.js文件
import qiankun from 'vite-plugin-qiankun'
export default defineConfig({
base:'/sub-vue'//和基座中配置的activeRule一致
server:{
port:3002,
cors:true,
origin:'http://localhost:3002'
},
plugins:{
vue(),
qiankun('sub-vue',{useDevMode:true})
}
})
- 修改main.ts入口文件
import {renderWithQiankun,qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
let app = null
if(!qiankunWindow._POWERED_BY_QIANKUN_){
createApp(App).use(router).mount('#app')
}else{
renderWithQiankun({
mount(props){
app = createApp(App)
app.use(router).mount(props.container.querySelector('#app'))
},
bootstrap(){},
update(){},
unmount(){app.unmount()}
})
}
umi框架的子应用
- npm i @umijs/plugins
- 配置.umirc.ts
export default {
base:'/sub-umi',
npmClient:'npm',
plugins:['@umijs/plugins/dist/qiankun'],
qiankun:{
slave:{}
}
}
如果想要在qiankun的生命周期做一些处理,在入口加入
export const qiankun ={
async mount(){},
async bootstrap(){},
async afterMount(){}
}
补充
- 样式隔离:
qiankun实现的是子应用之间的样式隔离,但是基座与子应用之间的样式隔离病没有实现,所以基座和子应用之间还是会存在样式的覆盖和冲突。
解决办法:
- 每个应用的样式使用固定格式
- 通过css-module的方式给每个应用自动加上前缀
- 子应用之间的相互跳转
(1)主应用和微应用都是hash模式,主应用根据hash来判断应用,则不用考虑这个
问题。
(2)history模式下微应用之间的跳转,或者微应用跳主应用页面,直接使用微应用的路由是不行的,原因是微应用的路由实例跳转都是基于路由的base。有两种方法可以跳转:
①history.pushState()
②将主应用的路由实例通过props传递给微应用,微应用这个路由实例跳转。 - 公共依赖加载
主应用和子应用都是用了相同的库(antd,axios),就可以使用externals的方式引入,减少加载重复包,导致的资源浪费。一个项目使用后,另外一个项目不必再重复加载。
方法:
主应用:将所有公共依赖配置webpack的externals,并在index.html中使用外链引入这些公共依赖。
子应用:和主应用一样配置webpack的externals,并且在index.html中引入这些公共依赖,还需要给子应用的公共依赖加上ignore属性(自定义属性,非标准属性),qiankun在解析时,如果发现ignore属性,就会自动忽略。 - 全局状态管理
一般来说,各个子应用通过业务线划分,不同业务线应该降低耦合度,尽量避免通信,但是涉及到一些公共状态或者操作,qiankun也是支持的。
qiankun提供了一个全局的GlobalState来共享数据,基座初始化之后,子应用可以监听到这个数据的变化,也能提交这个数据。
基座:
//基座初始化
const state ={count:1}
import {initGlobalState} from 'qiankun'
const actions = initGlobalState(state)
actions.onGlobalStateChange((state,prev)=>{
console.log(state,prev)
})
actions.setGlobalState(state)
子应用:
export function mount(props){
props.onGlobalStateChange((state,prev)=>{
console.log(state,prev)
})
props.setGlobalState(state)
}
【信息由网络或者个人提供,如有涉及版权请联系COOY资源网邮箱处理】
© 版权声明
部分内容为互联网分享,若有侵权请联系站长删除。
THE END
暂无评论内容