总览
今天看到了一篇觉得写得特别好的文章
基于wujie的解决方案来简单聊聊微前端 (2022年团队内分享PPT)因为目前有时间了,所以在整理一下自己这几年写过的 - 掘金 (juejin.cn)
讲解了目前几种主要的微前端的优劣,然后分析了一下wujie怎么解决iframe原生自带的一些缺陷
iframe处理微前端是最近常见的一种方案,优缺点都很明显:
优点:原生自带js和css隔离,上手简单,天然的html entry,可以实现多项目集成
缺点:
- 子应用保活
- 通信基本依赖postmessage
- 路由状态刷新后不可逆得丢失
- 子应用弹窗等无法做到全屏,会受到iframe边界的影响
- 每次加载都得拉子应用资源,导致白屏时间长,预加载实现困难
- http的子应用无法嵌入https里面
iframe
之前做了一个项目是系统管理控制租户,分配给各个子iframe页,获取对应的业务群组,再由业务群组去层层管控权限,算是一个权限管控落地的工作。
这里记录一下里面iframe通信流程,一共分为两个部分,分别是系统管理与子iframe微前端服务。
结构组成
系统管理:
- Nav导航栏,实际获取租户管控信息的组件;
- service组件,实际iframe实例化组件,同时负责系统管理与微前端的通信
子iframe微前端服务:
- 业务群组组件,被租户控制数据展现,又继而权限控制页面展现;
- iframeInteraction.js,通信js文件
- axios.js,接口拦截器统一管控权限限制信息
- 路由跳转管理js文件,用于实际路由页面展示
- 实际路由功能页
通信流程
使用的通信方法主要是postmessage,主要包括两种case
-
第一次打开的是系统管理页
-
第一次打开的是子iframe页
wujie
wujie处理以上问题本质上是因为将webcomponent和iframe结合在了一起,通过webcomponent装载html页面,iframe执行js代码,然后将iframe内的document的实体this指向webcomponent,webcomponent本质上只是一个组件,与主页面共享路由,路由状态丢失的问题就不会存在了
webcomponent
感觉这个webcomponent很有意思,原生实现隔离,那么我理解react的组件可以webcomponent封装提供给vue的框架使用,实现框架层面的共通
让豆包提供一个最简单的sample试试
先起项目
# 初始化项目(若已有 React 项目可跳过)
mkdir react-to-webcomponent && cd react-to-webcomponent
npm init -y # 安装核心依赖
npm install react react-dom # React 核心
npm install webpack webpack-cli babel-loader @babel/core @babel/preset-react @babel/preset-env # 打包与转译
npm install style-loader css-loader # 处理 React 组件样式
npm install @webcomponents/react-wrapper # React 转 WebComponent 适配工具(可选,简化适配)
// src/HelloReact.jsx
import React from 'react';
const HelloReact = ({ name }) => {
return (
<div>Hello from React! Name: {name}</div>;
)
};
export default HelloReact;
// src/HelloReactWebComponent.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import HelloReact from './HelloReact';
class HelloReactWebComponent extends HTMLElement {
static get observedAttributes() {
return ['name'];
}
constructor() {
super();
//这里豆包提供了一个错误的代码this.shadowRoot = this.attachShadow({ mode: 'open' });
this.attachShadow({ mode: 'open' });
this.props = { name: '默认名' };
}
connectedCallback() {
this.render();
}
attributeChangedCallback(name, old, newVal) {
this.props[name] = newVal;
this.render();
}
render() {
this.shadowRoot.innerHTML = '';
ReactDOM.createRoot(this.shadowRoot).render();
}
}
customElements.define('hello-react', HelloReactWebComponent);
const path = require('path');
module.exports = {
entry: './src/HelloReactWebComponent.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'hello-react.js',
libraryTarget: 'umd',
globalObject: 'this'
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic'
}
]
]
}
exclude: /node_modules/ }
]
},
resolve: {
extensions: ['.js', '.jsx']
}
}
然后开始执行npm run build,dist生成最终文件,将最终文件放到需要引入的vue的库中
import { onMounted } from 'vue';
onMounted(() => {
import('../public/hello-react.js');
});
如果需要实现外部vue项目能修改react文件,可以针对react文件进行改造
// src/HelloReact.jsx
import React from 'react';
const HelloReact = ({ name }) => {
return (
<div>Hello from React! Name: {name}</div>;
)
};
export default HelloReact;
vue项目的css直接更改
hello-react::part(conatiner){
color:blue
}
最简单demo就走完了,实测可以通过webcomponent将react无阻碍嵌入vue