微前端心得经验谈

44 阅读3分钟

总览

今天看到了一篇觉得写得特别好的文章

基于wujie的解决方案来简单聊聊微前端 (2022年团队内分享PPT)因为目前有时间了,所以在整理一下自己这几年写过的 - 掘金 (juejin.cn)

讲解了目前几种主要的微前端的优劣,然后分析了一下wujie怎么解决iframe原生自带的一些缺陷

iframe处理微前端是最近常见的一种方案,优缺点都很明显:

优点:原生自带js和css隔离,上手简单,天然的html entry,可以实现多项目集成

缺点:

  1. 子应用保活
  2. 通信基本依赖postmessage
  3. 路由状态刷新后不可逆得丢失
  4. 子应用弹窗等无法做到全屏,会受到iframe边界的影响
  5. 每次加载都得拉子应用资源,导致白屏时间长,预加载实现困难
  6. http的子应用无法嵌入https里面

iframe

之前做了一个项目是系统管理控制租户,分配给各个子iframe页,获取对应的业务群组,再由业务群组去层层管控权限,算是一个权限管控落地的工作。

这里记录一下里面iframe通信流程,一共分为两个部分,分别是系统管理与子iframe微前端服务。

结构组成

系统管理:

  1. Nav导航栏,实际获取租户管控信息的组件;
  2. service组件,实际iframe实例化组件,同时负责系统管理与微前端的通信

子iframe微前端服务:

  1. 业务群组组件,被租户控制数据展现,又继而权限控制页面展现;
  2. iframeInteraction.js,通信js文件
  3. axios.js,接口拦截器统一管控权限限制信息
  4. 路由跳转管理js文件,用于实际路由页面展示
  5. 实际路由功能页
通信流程

使用的通信方法主要是postmessage,主要包括两种case

  1. 第一次打开的是系统管理页

  2. 第一次打开的是子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 # ReactWebComponent 适配工具(可选,简化适配)
// 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