微前端(micro-app)使用手册

news2024/11/15 17:19:37

转载请注明出处,点击此处 查看更多精彩内容

micro-app 使用手册

micro-app 是借鉴了 Web Component 的思想,通过 Custom Element 结合自定义的 Shadow Dom,将微前端封装成一个类 Web Component 组件,从而实现微前端的组件化渲染。并且由于自定义 Shadow Dom 的隔离特性,micro-app 不需要像 single-spaqiankun 一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改 Webpack 配置,是目前市面上接入微前端成本最低的方案。

概念图

概念图

快速上手

主应用

主应用不限技术栈,只需引入 micro-app、配置子应用路由并启动 micro-app 即可。这里以 Vue3 框架为例。

安装 micro-app

yarn add @micro-zoe/micro-app

pnpm add @micro-zoe/micro-app

npm i @micro-zoe/micro-app

启动 micro-app

在应用入口引入并启动 micro-app

import microApp from '@micro-zoe/micro-app'

microApp.start()

嵌入子应用

创建 Vue 页面(如 src/views/SubApp.vue)用于承载子应用。

<template>
  <div>
    <micro-app name='sub-app' url='http://localhost:8381/' baseroute='/sub-app'></micro-app>
  </div>
</template>

<micro-app> 组件配置说明:

  • name: 子应用名称。必须以字母开头,且不可以带有除中划线和下划线外的特殊符号,每个 name 都对应一个应用,当多个应用同时渲染时,name 不可以重复。
  • url: 子应用地址。会被自动补全,如 http://localhost:3000/index.html。
  • baseroute: 主应用分配给子应用的基础路由。
  • 查看更多子应用配置

配置子应用路由

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    ...,
    {
      path: '/sub-app/*',
      component: () => import('../views/SubApp.vue')
    },
  ]
})

export default router

path 是子应用路由地址。非严格匹配,/sub-app/* 都指向 SubApp 页面。使用 vue-router@4.x 时写法为:'/sub-app/:page*'

Vue2 + Webpack 子应用

设置基础路由

const router = new VueRouter({
  mode: "history",
  routes,
  base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,
});

配置跨域支持

修改 vue.config.js 配置跨域支持。

const { defineConfig } = require("@vue/cli-service");

module.exports = defineConfig({
  ...,
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    ...,
  },
});

Vue3 + Vite 子应用

在嵌入 Vite 子应用时,micro-app 的功能只负责渲染,其它的行为由应用自行决定,这包括如何防止样式、JavaScript 变量、元素的冲突。

子应用的修改

  1. 添加自定义插件
  • 新建插件 vite-plugin-micro-app.js
import fs from 'fs'
import path from 'path'

function VitePluginMicroApp() {
  let basePath = ''
  return {
    name: 'vite:micro-app',
    apply: 'build',
    configResolved(config) {
      basePath = `${config.base}${config.build.assetsDir}/`
    },
    writeBundle(options, bundle) {
      for (const chunkName in bundle) {
        if (!Object.prototype.hasOwnProperty.call(bundle, chunkName)) {
          continue
        }
        const chunk = bundle[chunkName]
        if (!chunk.fileName?.endsWith('.js') && !chunk.fileName?.endsWith('.ts')) {
          continue
        }
        chunk.code = chunk.code.replace(/(from|import\()(\s*['"])(\.\.?\/)/g, (all, $1, $2, $3) =>
          all.replace($3, new URL($3, basePath))
        )
        const fullPath = path.join(options.dir, chunk.fileName)
        fs.writeFileSync(fullPath, chunk.code)
      }
    }
  }
}

export default VitePluginMicroApp
  • 导入插件并配置公共资源基础路径
import microAppPlugin from './vite-plugin-micro-app'

export default defineConfig({
  ...,
  base: '/vue3-app/',
  plugins: [vue(), microAppPlugin()],
})
  1. 修改容器元素 id
  • 修改 index.html 中容器元素的 id 值
<body>
  <div id="my-vite-app"></div>
</body>
  • 使用新的 id 渲染
createApp(App).mount('#my-vite-app')

当多个vite子应用同时渲染时,必须修改容器元素的id值,确保每个子应用容器元素id的唯一性,否则无法正常渲染。

  1. 路由

推荐基座使用 history 路由,Vite 子应用使用 hash 路由,避免一些可能出现的问题。

子应用如果是 Vue3,在初始化时路由时,createWebHashHistory 不要传入参数,如下:

const router = createRouter({
  ...,
  history: createWebHashHistory(),
})
  1. 静态资源

图片等静态资源需要使用绝对地址,可以使用 new URL('../assets/logo.png', import.meta.url).href 等方式获取资源的全链接地址。

主应用的修改

  1. 关闭沙箱并使用内联 script 模式
<micro-app
  name='child-name'
  url='http://localhost:3001/basename/'
  disableSandbox // 关闭沙箱
  inline // 使用内联script模式
>
  1. 处理子应用静态资源

写一个简易的插件,对开发环境的子应用进行处理,补全静态资源路径。

microApp.start({
  plugins: {
    modules: {
      // appName 即子应用的 name
      appName: [{
        loader(code) {
          if (import.meta.env.MODE !== 'development') {
            return code
          }
          // 这里 basename 需要和子应用vite.config.js中base的配置保持一致
          code = code.replace(/(from|import)(\s*['"])(\/basename\/)/g, all => {
            return all.replace('/basename/', '子应用域名/basename/')
          })
          return code
        }
      }]
    }
  }
})

React 子应用

设置基础路由

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter basename={window.__MICRO_APP_BASE_ROUTE__ || "/"}>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
);

配置跨域支持

  1. 安装 react-app-rewired customize-cra 依赖
yarn add -D react-app-rewired customize-cra

pnpm add -D react-app-rewired customize-cra

npm i -D react-app-rewired customize-cra
  1. 应用根目录添加 config-overrides.js 文件
const { overrideDevServer } = require("customize-cra");

module.exports = {
  devServer: overrideDevServer((config) => ({
    ...config,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  })),
};
  1. 修改 package.json
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",

应用间通信

micro-app 提供了一套灵活的数据通信机制,方便主应用和子应用之间的数据传输。

正常情况下,主应用和子应用之间的通信是绑定的,主应用只能向指定的子应用发送数据,子应用只能向基座发送数据,这种方式可以有效的避免数据污染,防止多个子应用之间相互影响。

同时 micro-app 也提供了全局通信,方便跨应用之间的数据通信。

主应用向子应用发送数据

主应用向子应用发送数据有两种方式。

  1. 通过 data 属性发送数据

使用 <micro-app> 组件的 data 给子应用发送数据,此时只接受对象类型,数据变化时会自动重新发送。

<template>
  <div>
    <micro-app name="my-app" url="http://localhost:8381/" baseroute="/my-app" :data="data" />
  </div>
</template>

<script setup lang="ts">
const data = { msg: '通过 data 发送给子应用的数据' }
</script>
  1. 手动发送数据

手动发送数据需要通过 name 指定接受数据的子应用,此值和 <micro-app> 元素中的 name 一致。

// 发送数据给子应用 my-app,setData第二个参数只接受对象类型
microApp.setData('my-app', { msg: '手动发送给子应用的数据' })

子应用接收主应用发送的数据

micro-app 会向子应用注入名称为 microApp 的全局对象,子应用通过这个对象有两种方式获取来自主应用的数据。

  1. 直接获取
// 获取主应用下发的 data 数据
const data = window.microApp.getData()
  1. 绑定监听函数
function dataListener(data) {
  console.log('来自主应用的数据', data)
}

/**
 * 绑定监听函数,监听函数只有在数据变化时才会触发
 * dataListener: 绑定函数
 * autoTrigger: 在初次绑定监听函数时如果有缓存数据,是否需要主动触发一次,默认为 false
 * !!!重要说明: 因为子应用是异步渲染的,而基座发送数据是同步的,
 * 如果在子应用渲染结束前主应用发送数据,则在绑定监听函数前数据已经发送,在初始化后不会触发绑定函数,
 * 但这个数据会放入缓存中,此时可以设置 autoTrigger 为 true 主动触发一次监听函数来获取数据。
 */
window.microApp.addDataListener(dataListener: Function, autoTrigger?: boolean)

// 解绑监听函数
window.microApp.removeDataListener(dataListener: Function)

// 清空当前子应用的所有绑定函数(全局数据函数除外)
window.microApp.clearDataListener()

子应用向主应用发送数据

// dispatch只接受对象作为参数
window.microApp.dispatch({ msg: '子应用发送的数据' })

主应用接收子应用发送的数据

主应用获取来自子应用的数据有三种方式。

  1. 直接获取数据
// 获取指定子应用发送的数据
const childData = microApp.getData(appName)
  1. 监听 datachange 事件
<template>
  <div>
    <micro-app name="my-app" url="http://localhost:8381/" baseroute="/my-app" @datachange="handleDataChange" />
  </div>
</template>

<script setup lang="ts">
function handleDataChange(data: any) {
  console.log(data);
}
</script>
  1. 绑定监听函数
function dataListener(data) {
  console.log('来自子应用的数据', data)
}

/**
 * 绑定监听函数
 * appName: 应用名称
 * dataListener: 绑定函数
 * autoTrigger: 在初次绑定监听函数时如果有缓存数据,是否需要主动触发一次,默认为false
 */
microApp.addDataListener(appName: string, dataListener: Function, autoTrigger?: boolean)

// 解绑监听my-app子应用的函数
microApp.removeDataListener(appName: string, dataListener: Function)

// 清空所有监听appName子应用的函数
microApp.clearDataListener(appName: string)

全局数据通信

全局数据通信会向主应用和所有子应用发送数据,在跨应用通信的场景中适用。

发送全局数据

// setGlobalData 只接受对象作为参数
microApp.setGlobalData({ msg: '全局数据' })

获取全局数据

  1. 直接获取数据
const globalData = window.microApp.getGlobalData()
  1. 绑定监听函数
const dataListener = data => {
  console.log(data)
}
microApp.addGlobalDataListener(dataListener, true)

关闭沙箱后的通信方式

沙箱关闭后,子应用默认的通信功能失效,此时可以通过手动注册通信对象实现一致的功能。

注册方式:在主应用中为子应用初始化通信对象

import { EventCenterForMicroApp } from '@micro-zoe/micro-app'

// 注意:每个子应用根据 appName 单独分配一个通信对象
window.eventCenterForAppxx = new EventCenterForMicroApp(appName)

子应用通信方式:

// 直接获取数据
const data = window.eventCenterForAppxx.getData()


function dataListener(data) {
  console.log('来自主应用的数据', data)
}

/**
 * 绑定监听函数
 * dataListener: 绑定函数
 * autoTrigger: 在初次绑定监听函数时如果有缓存数据,是否需要主动触发一次,默认为 false
 */
window.eventCenterForAppxx.addDataListener(dataListener: Function, autoTrigger?: boolean)

// 解绑监听函数
window.eventCenterForAppxx.removeDataListener(dataListener: Function)

// 清空当前子应用的所有绑定函数(全局数据函数除外)
window.eventCenterForAppxx.clearDataListener()

// 子应用向主应用发送数据,只接受对象作为参数
window.eventCenterForAppxx.dispatch({ msg: '子应用发送的数据' })

常见问题

__webpack_public_path__ 无效,静态资源路径错误。

public-path.js 的导入语句放在应用入口文件的第一行。

TypeScript cannot find name __webpack_public_path__

src 目录新增 global.d.ts 文件:

declare let __webpack_public_path__: string;

interface Window {
  __MICRO_APP_BASE_ROUTE__: string;
  __MICRO_APP_PUBLIC_PATH__: string;
  __MICRO_APP_ENVIRONMENT__: boolean;
}

React18 子应用首次进入展示空白,再次进入正常。

React18 项目中使用 @rescripts/cli 修改 Webpack 配置可能会导致 micro-app 首次进入 React18 子应用时展示空白。

@rescripts/cli 替换为 react-app-rewired customize-cra 即可。

configuration.output has an unknown property ‘jsonpFunction’.

output.jsonpFunction 更名为 output.chunkLoadingGlobal​​​​​​​

// jsonpFunction: `webpackJsonp_${name}`,
chunkLoadingGlobal: `webpackJsonp_${name}`,

子应用内部路由跳转后无法切换到主应用或其他子应用且路由栈异常。

  • Vue 通过路由守卫更新 state
router.afterEach(() => {
  // Vue2
  Object.assign(history.state, { current: location.pathname });
  // Vue3
  Object.assign(history.state, {
    current: history.state.current === '/' ? '/' : `${location.pathname}${location.hash}`
  });
});
  • React 通过轮询监听路由变化更新 state
function listenRouterChange(callback: () => void) {
  let oldHref = window.location.href;
  const loop = () => {
    window.requestAnimationFrame(() => {
      if (window.location.href !== oldHref) {
        oldHref = window.location.href;
        callback();
      } else {
        loop();
      }
    });
  };
  loop();
}

listenRouterChange(() => {
  Object.assign(window.history.state, { current: window.location.pathname });
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/683136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目集活动—项目集交付阶段活动

项目集交付阶段活动包括协调和管理项目集实际交付所需执行的项目集活动。这些活动包括围绕 变更控制、报告和信息发布所开展的活动&#xff0c;以及围绕成本、采购、质量和风险所开展的活动。 这些活动提供了贯穿整个项目集生命周期的支持活动和流程&#xff0c;旨在提供项目集…

第十三章 Transformer注意力机制

系列文章目录 第一章 AlexNet网络详解 第二章 VGG网络详解 第三章 GoogLeNet网络详解 第四章 ResNet网络详解 第五章 ResNeXt网络详解 第六章 MobileNetv1网络详解 第七章 MobileNetv2网络详解 第八章 MobileNetv3网络详解 第九章 ShuffleNetv1网络详解 第十章…

日志分析篇之Linux日志分析

0x00 前言 Linux系统拥有非常灵活和强大的日志功能&#xff0c;可以保存几乎所有的操作记录&#xff0c;并可以从中检索出我们需要的信息。 本文 简介一下Linux系统日志及日志分析技巧。 0x01 日志简介 日志默认存放位置&#xff1a;/var/log/ 查看日志配置情况&#xff1a;…

抖音本地生活林客服务商开通

抖音林客系统是一种面向本地生活服务的平台&#xff0c;它将消费者与商家联系在一起&#xff0c;在提供更便捷的服务的同时也创造了商业机会。考虑到目前互联网和移动设备的普及程度&#xff0c;以及人们对于生活质量和便利性的不断追求&#xff0c;抖音林客系统具有广阔的市场…

【AUTOSAR】AUTOSAR开发工具链(十)----基于BTC的MIL/SIL测试操作说明(2)

三、PowerWindow demon SIL测试 <一>、如果是手写的纯C代码使用方法如下 创建C代码工程文件 添加C代码的工程文件和不在一个文件夹的头文件路径 选择测试结构 接口参数格式定义 设置采样时间等于调度周期 点击import,添加测试用例类似于MIL测试 <二>、如果是模型…

小航助学2023年6月GESP_C++二级试卷(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 单选题2.0分 删除编辑附件图文 答案:D 第1题高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在…

这篇文章让你彻底了解什么是安全测试?

目录 前言 安全测试的验证点&#xff1a; 主要归结为以下几点&#xff1a;&#xff08;后期可以优化成一个安全测试的框架结构&#xff09; 总结&#xff1a; 前言 web应用无处不在&#xff0c;存在于每个行业&#xff0c;现在的发展速度非常快速&#xff0c;且web应用在软…

Jetpack Compose实现的一个优雅的 Toast 组件——简单易用~

Toast 是Android中常见的轻量级提示 本文将介绍如何使用Compose技术实现一个Toast组件 不是一个简单的toast 优雅-简洁-动画 才是我的风格 系统原生的Toast默认是在底部弹出&#xff0c;配合kotlin语音的特性&#xff0c;简单封装一下&#xff0c;使用方法非常简洁 inline …

CodeForces..好数列.[简单].[数学规律]

题目描述&#xff1a; 题目解读&#xff1a; 给定n&#xff0c;k 数组a是由0和1组成的&#xff0c;长度为n的数组&#xff0c;数组满足&#xff1a; a的前i个元素中至少有i/k个等于1&#xff1b; a的后i个元素中至少有i/k个等于1&#xff1b; 输入n,k 输出满足条件的数组的…

【JAVA】学生信息管理系统

目录 前言 一、环境搭建 二、功能实现 1.学生信息类的创建 2.学生信息的添加功能 3.学生信息的删除功能 4.学生信息的修改功能 5.学生信息的查看功能 三、主类的调用 1.界面的搭建 2.学生端和教师端 3.系统和功能的选择 总结 前言 JAVA实现的学生信息管理…

8.6 socket套接字及TCP的实现框架

socket套接字 目录 socket套接字 体系结构的两种形式 几种常见的网络编程接口 socket套接字 socket常用API介绍 socket套接字 三元组【IP地址&#xff0c;端口&#xff0c;协议】 地址族结构体 套接字类型 TCP通信的实现过程 体系结构的两种形式 网络的体系结构 (N…

保护云环境:云渗透测试和安全策略探究

随着云计算技术的快速发展&#xff0c;越来越多的组织将他们的数据和应用程序迁移到云端。然而&#xff0c;与此同时&#xff0c;云安全也面临着新的挑战。云渗透测试是一种评估云环境安全性的方法&#xff0c;它帮助组织发现并解决可能存在的漏洞和弱点。在本文中&#xff0c;…

docker - 部署java/python项目

目录 1、docker - 部署 java 项目 1. 创建 mysql 容器 2. 验证mysql&#xff0c;dbserver 连接 mysql &#xff0c;服务器ip:3306 ,账户&#xff1a;root 密码&#xff1a;123456 3. 创建tomcat容器 4. 验证&#xff1a;http://ip:80 5. 开发项目war包&#xff0c;放到to…

【MOOC 作业】第2章 应用层

不是标答也不是参考答案 仅从个人理解出发去做题 1、(20分) 什么是持久性连接&#xff1f;什么是非持久性连接&#xff1f;持久性连接与非持久性连接区别如何&#xff1f; 持久性连接&#xff1a;每个请求/响应对都经同一个 TCP 连接发送。非持久性连接&#xff1a;每个请求/…

【1401. 圆和矩形是否有重叠】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个以 (radius, xCenter, yCenter) 表示的圆和一个与坐标轴平行的矩形 (x1, y1, x2, y2) &#xff0c;其中 (x1, y1) 是矩形左下角的坐标&#xff0c;而 (x2, y2) 是右上角的坐标。 如果圆和矩…

灰狼优化算法GWO,优化VMD,适应度函数为最小样本熵或最小包络熵(可自行选择,代码已集成好,很方便修改)包含MATLAB源代码

近期评论区有小伙伴私信需要灰狼优化算法GWO法化VMD的&#xff0c;所以打算再写一篇。 与之前的文章不同&#xff0c;这篇文章作者考虑到&#xff0c;大家有可能会以最小样本熵或最小包络熵为适应度函数的&#xff0c;在这个程序中将会直接把样本熵和包络熵集成&#xff0c;在…

了解Facebook的算法如何工作,提高您的帖子曝光率

Facebook是全球最大的社交媒体平台之一&#xff0c;每天有数亿用户在上面发布内容、互动交流。然而&#xff0c;由于用户数量巨大&#xff0c;每天产生的信息量也是相当惊人的。 为了让用户看到最有用和最相关的内容&#xff0c;Facebook采用了一种复杂的算法来过滤和排序用户…

迅为RK3568开发板Buildroot 系统设置状态栏

Weston 支持在 weston.ini 配置文件的 shell 段设置状态栏的背景色、位置,以及在 launcher 段设置快捷启动程序&#xff0c;修改文件系统的/etc/xdg/weston/weston.ini 文件&#xff0c;如下所示&#xff1a; [shell] # 颜色格式为 ARGB8888 panel-color0xff002244 # top|…

【新星计划回顾】第八篇学习-多表联表查询(完结)

&#x1f3c6;&#x1f3c6;时间过的真快&#xff0c;这是导师回顾新星计划学习的第八篇文章&#xff01; 在学习过程中&#xff0c;学员们也咨询了很多问题&#xff0c;我把一些问题整理以文章形式总结分享下。 最近这段时间非常忙&#xff0c;虽然导师首次参与新星计划活动已…

[内核笔记1]内核文件结构与缓存——inode和对应描述

由来&#xff1a;公司内部外网记录日志的方式现在都是通过Nginx模块收到数据发送到系统消息队列&#xff0c;然后由另外一个进程来从消息队列读取然后写回磁盘这样的操作&#xff0c;尽量的减少Nginx的阻塞。 但是由于System/V消息队列在使用前需要规定消息长度&#xff0c;且…