从0到1落地微前端架构 001【学习笔记】

微前端

js 隔离
css 隔离
元素隔离
生命周期
预加载
数据通信
应用跳转
多层嵌套


说明

  • 使用的是 Mermaid 的 flowchart 语法,Markdown 渲染器如 Typora、VitePress、一些 Git 平台都支持。
  • 保留了:
  • 基座应用 main-vue3
  • 各子应用:child-nuxt2-homechild-vue2-jobchild-vue3-enterprisechild-react18-about
  • 框架的核心逻辑,如 JS 隔离、样式隔离、生命周期、应用通信、虚拟路由等
  • 右侧的初始化流程

◆ 多种前沿技术栈
◆ 多项目实操,一人分饰几角
◆ 微前端应用场景、落地方案
◆ 巨石应用开发流程
◆ 架构思维、架构能力
◆ 打造企业级微前端应用

两个问题

  • 什么是微前端
  • 背景 屎山代码(开发、运行、打包,但又不能将其废掉,还要把需求增量到项目中)【巨石应用】
  • 基本概念 最早出现在 2016 年(借鉴了微服务的架构)
  • 核心思想 容器应用(主应用)子应用(微应用)
  • 使用场景
    • 增量升级 减少冲突 提高效率
    • 灵活性,(技术栈无关)可以使用不同的构建工具
    • 稳定性 各微应用各司其职,聚合平台
    • 独立性 独立开发、测试、部署
  • iframe single-spa qiankun micro-app
    • <iframe src="https://www.example.com" sandbox></iframe> 简单易用,天然沙箱(完全隔离),隔离太完美,刷新即丢失(白屏时间长加载速度慢)
  • single-spa 是最早的微前端框架(单页面微前端模式),在基座上维护一个基座路由表,微前端框架鼻祖,改造成本大,沙箱不完美,应用通信能力差(单页面),不支持 esmodule,所以也不支持 vite

    ```js
    // 1. 注册
    // 2. 加载
    import { registerApplication } from 'single-spa';

    registerApplication({
    name: 'app',
    app: () => {
    loadScripts('./chunk-a.js');
    loadScripts('./chunk-b.js');
    return loadScripts('./entry.js');
    },
    });

    singleSpa.start();
    ```

  • Qiankun 基本 single-spa 也有提升

    • 它是通过 html entry
    • 更完备的沙箱方案
    • 适配成本高,同样对基座和子应用做一些改动,不支持 vite
  • Micro-app 京东技术团队

    • 低侵入性(适配成本低),开箱即用
    • 文档易读
    • 更好的兼容性(支持 webpack,vite)
  • 为什么要学习

现代微前端架构理论

之前通过 nginx 配置不同的入口来实现

  1. 团队自治 跨多团队合作开发困难
  2. 核心思想 开发、部署成本
  3. 场景落地 系统的渐进性、动态性
graph LR
  主分支[主分支] -->|git仓库| 分支1
  主分支 --> 分支2
  主分支 --> 分支3
  主分支 --> 分支4

  分支1 --> 团队1
  团队1 -->|commit| 赵 --> 钱 --> 孙

  分支2 --> 团队2
  团队2 -->|commit| 李 --> 周 --> 吴

  分支3 --> 团队3
  团队3 -->|commit| 郑 --> 王 --> 冯

  分支4 --> 团队4
  团队4 -->|commit| 陈 --> 褚 --> 卫

每个仓库都会进行独立的部署

graph LR
  主分支[主分支] -->|git仓库| 团队1仓库
  主分支 --> 团队2仓库
  主分支 --> 团队3仓库
  主分支 --> 团队4仓库

  团队1仓库 -->|commit| 赵 --> 钱 --> 孙
  团队2仓库 -->|commit| 李 --> 周 --> 吴
  团队3仓库 -->|commit| 郑 --> 王 --> 冯
  团队4仓库 -->|commit| 陈 --> 褚 --> 卫

独立开发(每个应用都是一个独立的应用)

技术选型

微前端框架技术框架无关,我们可以选择任何前端的技术框架

微前端应用 = 微前端框架 + JS 框架 + UI 框架 + 构建工具

  • 微前端框架:Micro-app
  • JS 框架:Vue3、Vue2、Nuxt2、React18
  • UI 框架:Element-ui、Element-plus
  • 构建工具:Webpack5、Vite4、Vue-cli5

整体架构思路为 CustomElement + HTMLEntry:

  • micro-app 标签:上可以设置各种配置,比如开启 iframe 沙箱、开启 SSR 模式、开启 keep-alive 模式、关闭沙箱、数据通信。
  • HTMLEntry:就是以 HTML 文件作为入口地址进行渲染。

主要功能:

生命周期、环境变量、虚拟路由、js 沙箱、样式隔离、元素隔离、数据通信等等

生命周期

  • created<micro-app> 标签初始化后,加载资源前触发。
  • beforemount:加载资源完成后,开始渲染之前触发。
  • mounted:子应用渲染结束后触发。
  • unmount:子应用卸载时触发。

环境变量

  • __MICRO_APP_PUBLIC_PATH__
  • __MICRO_APP_BASE_ROUTE__

虚拟路由系统

通过虚拟路由系统,我们可以方便地进行导航守卫、跨应用的跳转,提升开发效率,并且子应用运行在这套虚拟路由系统中,和主应用的路由进行隔离,避免相互影响,如:

  • 主应用控制子应用跳转
  • 子应用控制主应用跳转
  • 子应用控制其它子应用跳转

js 沙箱

确保子应用之间 全局变量/事件不冲突

样式隔离

确保子应用之间样式互相不干扰

.test {
  color: red;
}

/* 转换为 */
micro-app[name='xxx'] .test {
  color: red;
}

元素隔离

元素隔离的概念来自 ShadowDom,即 ShadowDom 中的元素可以和外部的元素重复但不会冲突。micro-app 模拟实现了类似 ShadowDom 的功能:

  • 元素不会逃离 <micro-app> 元素边界。
  • 子应用只能对自身的元素进行增、删、改、查的操作。

通信

主子通信
子应用全局通信

其它能力

预加载,缓存等

系统架构

graph TD
    A[基座应用 main-vue3] -->|登录| B[micro-app 框架逻辑]
    B --> C[JS 隔离]
    B --> D[样式隔离]
    B --> E[元素隔离]
    B --> F[生命周期]
    B --> G[应用通信]
    B --> H[虚拟路由系统]
    B --> I[预加载]
    B --> J[资源地址补全]
    B --> K[...]

    B --> L[子应用 child-nuxt2-home]
    B --> M[子应用 child-vue2-job]
    B --> N[子应用 child-vue3-enterprise]
    B --> O[子应用 child-react18-about]

    P[初始化 micro-app] --> Q[嵌入子应用]
    Q --> R[通用组件]
    R --> S[统一鉴权]

1-1.png

4 个子应用和 1 个主应用

nvm 对 nodejs 版本管理

什么是 nvm

nvm 是 node 的包管理工具,它可以帮助我们在不同的项目环境中使用不同的 node 版本,所以在启用不同项目时,可能遇到报错。

例如:如果我们本次教程的所使用到的 nuxt3 和 vite 搭建不同的项目,就是依赖于不同的 node 环境。

  • nuxt3 依赖的 node 版本 >= v14.16.0
  • vite >= 12.0.0

所以我们的电脑里需要配置两种 node,在我们当前的项目中使用对应的 node 环境,如何使得不同版本的 node 共存系统呢,请看下文。

nvm 的安装

下载

下载,选择某一个版本的 nvm,安装 nvm-setup.zip。

主子应用的功能分配

主应用:基座(main-vue3)

功能:拿到 token 然后派发到各个子应用

  • 登录 /api/auth/login POST
  • 登出 /api/auth/logout POST

接口说明:

  • 登录:传入账号和密码,返回 token。
  • 登出:清除 token。
// POST /api/auth/login
{
  "username": "test",
  "password": "123456"
}
// 返回
{
  "token": "mocked-token-123"
}

子应用:首页(child-nuxt2-home)

功能:

  • 首页列表 /api/home/list GET

mock 示例:

// GET /api/home/list
[
  { "id": 1, "title": "首页内容一" },
  { "id": 2, "title": "首页内容二" }
]

子应用:找工作(child-vue2-job)

功能:

  • 职位列表 /api/job/list GET
  • 职位详情 /api/job/detail/:id GET

mock 示例

// GET /api/job/list
[
  { "id": 101, "title": "前端开发", "company": "阿里巴巴" },
  { "id": 102, "title": "后端开发", "company": "腾讯" }
]

// GET /api/job/detail/101
{
  "id": 101,
  "title": "前端开发",
  "description": "负责前端页面开发",
  "company": "阿里巴巴"
}

子应用:找企业(child-vue3-job)

功能:

  • 企业列表 /api/company/list GET
  • 企业详情 /api/company/detail/:id GET

mock 示例:

// GET /api/company/list
[
  { "id": 201, "name": "字节跳动", "industry": "互联网" },
  { "id": 202, "name": "百度", "industry": "AI" }
]

// GET /api/company/detail/201
{
  "id": 201,
  "name": "字节跳动",
  "industry": "互联网",
  "location": "北京"
}

子应用:关于我们(child-react18-about)

功能:

  • 关于我们 /api/about/info GET

mock 示例:

// GET /api/about/info
{
  "company": "微前端平台",
  "description": "一个多技术栈集成的企业级系统"
}
  1. Mock Server 工具推荐:
  2. 使用 Mock Service Worker (MSW)(适用于 Vue/React/Nuxt 等)
  3. 或者本地 node server + json-server/mockjs
  4. 接口统一管理:
  5. 每个子应用维护自己的接口配置
  6. 主应用统一配置公共接口(如认证相关)
  7. 前后端联调方式:
  8. 使用 axios 拦截器统一处理 token
  9. 子应用通过环境变量控制是否启用 mock

实现基座应用

目前使用 node(v18.20.6)

pnpm create vue@latest
# ts
# pina
# vue-router
cd micro-main-vue3
pnpm install
pnpm dev
# 安装相关依赖 router
# /router目录下放置路由
# /views目录下放视图
# /views/child目录下放子应用路由
# /views/main目录下放基座应用的视图
# 安装element plus
  1. 基座应用上集成 microapp
@micro-zoe/micro-app
  1. 项目的 main.js 中引入它并且初始它,具体实现看去 github 上看
import microApp from '@micro-zoe/micro-app';

microApp.start();

使用 nust2 实现首页应用(child-nuxt3-home)

它是基于 vue3 的服务端渲染项目(我学习的版本使用的是 vue3 我用的也是 nuxt3)

pnpm create nuxt-app child-nuxt3-home

功能开发完成后,集成到基座应用中,使用<micro-app name="child-app" url="http://localhost:3000/child-home"></micro-app>, 但它会提示跨域问题,我们可以在 chrome 中添加了一个插件来测试允许跨域,插件名:Allow CORS: Access-Control-Allow-Origin

使用 vue2 构建一个子应用找工作(micro-child-vue2-job)

micro-child-vue2-job/
├── public/
│ └── index.html
├── src/
│ ├── assets/
│ ├── components/
│ ├── views/
│ ├── App.vue
│ ├── main.js
│ └── router.js
├── .gitignore
├── babel.config.js
├── package.json
└── README.md

vue3 构建一个子应用找企业(micro-child-vue3-enterprise)

2024-04-12

接口 mock 调整及列表数据展示

功能改动

  1. 添加接口代理配置
  2. 新增 .env.development 文件配置环境变量
  3. vue.config.js 中添加代理配置,支持接口转发
  4. 配置 mock 接口地址:http://127.0.0.1:4523/m1/6202454-5895755-default
  5. 调整职位列表样式
  6. 移除列表容器背景色
  7. 优化职位卡片样式
  8. 调整字体大小和颜色
  9. 添加卡片悬停效果
  10. 优化标签样式
  11. 完善列表数据展示
  12. 展示职位基本信息(标题、薪资)
  13. 展示公司信息(Logo、名称、行业)
  14. 展示职位标签(地点、经验、学历)
  15. 展示福利标签
  16. 展示技能标签
  17. 添加分页功能

文件变更

  • 新增文件:
  • .env.development:环境配置文件
  • 修改文件:
  • src/api/findJobApi.js:添加接口调试日志
  • src/assets/scss/findjob.scss:调整样式
  • src/utils/request.js:调整请求配置
  • src/views/FindJob.vue:完善列表展示
  • vue.config.js:添加代理配置

接口数据结构

{
  "code": 200,
  "message": "success",
  "data": {
    "list": [
      {
        "jobId": "job-100",
        "jobTitle": "产品经理",
        "enterpriseName": "腾讯科技",
        "enterpriseLogo": "https://logo.clearbit.com/mi.com",
        "industry": "人工智能",
        "jobType": "全职",
        "education": "本科",
        "workCity": "北京市",
        "workExperience": "1年",
        "salaryMin": 10642,
        "salaryMax": 17846,
        "salaryRange": "10642-17846元/月",
        "salaryUnit": "月",
        "welfareTags": ["五险一金", "交通补贴", "加班补助"],
        "skillTags": ["Python", "TensorFlow", "PyTorch"],
        "refreshTimeStr": "2024年4月12日"
      }
    ],
    "total": 25
  }
}

样式规范

  • 主色调:#4e6ef2
  • 标题文字:16px, #333
  • 薪资文字:16px, #ff6b6b
  • 普通文字:13px, #666
  • 次要文字:12px, #999
  • 标签样式:
  • 背景色:#f8f9fc
  • 文字颜色:#666
  • 圆角:2px
  • 卡片样式:
  • 背景色:#fff
  • 内边距:24px
  • 圆角:4px
  • 阴影:0 1px 3px rgba(0, 0, 0, 0.02)

创建 react 应用

 npx create-react-app micro-child-react18-about

基于 React 18 的微前端子应用,支持独立运行和作为子应用运行。

项目结构

micro-child-react18-about/
├── public/                 # 静态资源目录
├── src/                    # 源代码目录
│   ├── components/         # 公共组件
│   │   └── Layout/        # 布局组件
│   ├── pages/             # 页面组件
│   │   └── About/         # 关于页面
│   ├── styles/            # 样式文件
│   │   └── global.scss    # 全局样式
│   ├── utils/             # 工具函数
│   │   └── request.js     # 请求封装
│   ├── App.js             # 应用入口组件
│   ├── index.js           # 应用入口文件
│   └── public-path.js     # 微前端环境配置
├── .env                    # 环境变量
├── package.json           # 项目依赖配置
└── README.md              # 项目说明文档

micro 沙箱体(js 隔离)

  1. new Fuction("return window")()
  2. (0,eval)("window")
  3. window.rawWindow

比如职位类型可能几个子应该都用到职位类型,我们可以将其提取到基座应该中,另外我们可以在子应用中通过window.__MICRO_APP_PUBLIC_PATH__
如果在主应用中显示 micro-app 不能识别的警告,但不影响使用我们需要在主应用中添加一些配置,在基座应用中 vite.config.js 中添加

//...
plugins: [
  vue({
    template: {
      compilerOptions: {
        isCustomElement: (tag) => /^micro-app/.test(tag),
      },
    },
  }),
];
// ..

使用基座应用数据的逻辑

mounted() {
  if (window.__MICRO_APP_ENVIRONMENT__) {
    // 微前端环境
    this.jobTypeArr = window.rawWindow.jobTypeArr;
  } else {
    this.jobTypeArr = jobTypeArr;
  }
  // this.getAddressDict();
  // this.searchJobList();
}

样式隔离(默认开启)

通过基座应该设置前缀来隔离,通过命名空间来

<el-config-provider namespace="ep">
  <common-header v-if="route.name !== 'login'" />
  <main-container />
  <common-footer v-if="route.name !== 'login'" />
</el-config-provider>

创建一个 scss 文件将

// styles/element/index.scss
// we can add this to custom namespace, default is 'el'
@forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
  $namespace: 'ep'
);
// ...

注释掉 main 中的引入import 'element-plus/dist/index.css' 换成 scss 文件

在 vite.config 配置中声明

css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@use "./src/styles/element/index.scss" as *;`,
    },
  },
},

元 s 隔离

shadow dom 元素可以重复,但又不会产生冲突,所有元素都不会逃离micro-app这个边界

生命周期(micro-app)

可以 start 上配置lifeCycles,也可以在每一个标准上使用@mounted来为每一个子应该设置

主子通信

micro-app标签上添加数据

import microApp from '@micro-zoe/micro-app'
const globalData = microApp.getGlobalData()
<micro-app :data="globalData"></micro-app>

在子应用项目中

if (window.__MICRO_APP_ENVIROMENT__) {
  const dataForchild = window.microApp.getData();
  // 获取数据对象
  const { token } = dataForChild;
  axios.defaults.headers['x-client-token'] = token;
}

也可以使用microApp.setData('childEnterprise',data) 来设置,这个名字要和micro-app的 name 保持一致

子应用向主应用发送数据

if (window.__MICRO_APP_ENVIRONMENT__) {
  window.microApp.dispatch({
    activeIndex: 'job',
  });
}
// 在基座应用中获取数据
<script setup>
function handleDataChange(e) {
  let { activeIndex } = e.detail.data;
  localStorage.setItem('activeIndex', activeIndex);
}
</script>

<template>
  <div>
    <micro-app
      @datachange="handleDataChange"
      name="childHome"
      url="http://localhost:3000/childHome/"
    ></micro-app>
  </div>
</template>

<style></style>

子应用间通信

在首页应用点击搜索,把关键字带入到找工作子应用

  1. 判断是不是微前端环境
  2. 把数据发给基座
searchIt() {
  let { homeSearchValue } = this;
  if (window.__MICRO_APP_ENVIRONMENT__) {
    window.microApp.setGlobalData({ homeSearchValue });
    const baseRouter = window.microApp.router.getBaseAppRouter();
    baseRouter.push('/main/childJob');
  }
},
// 应用启动时要注册其路由信息
microApp.router.setBaseAppRouter(router) //router是基座路由信息
// 在找工作子应用的mounted中把数据拿出来添加到查询接口的参数中
const globalData = window.microApp.getGlobalData()
if(globalData){

}

主应用跳转子应用,我们在基座应用的路由中已经处理过了,如何从子应用跳转到主应用(刷新页面跳,不刷新页面跳)

// 1.
window.microApp.location.href = '/main/login';
// 2
// 获取基座应用的路由然后来设置
const baseRouter = window.microApp.router.getBaseAppRouter();
baseRouter.push('/main/login');

微前端应用的优化

预加载:requestIdleCallback 在浏览器不忙的时候把资源加载一下,在基座应用中在 start({})对象中添加属性prefetchApps

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

microApp.start({
  prefetchApps: [
    {
      name: 'childHome',
      url: 'http://localhost:3000/childHome/',
      level: 3,
    },
    {
      name: 'childJob',
      url: 'http://localhost:8080',
      level: 3,
    },
    {
      name: 'childEnterprise',
      url: 'http://localhost:3002/child/findEnterprise/',
      level: 3,
      iframe: true,
    },
    {
      name: 'childAbout',
      url: 'http://localhost:3003/',
      level: 3,
    },
  ],
});

要和标签的配置保持一致

配前端的资源共享,加载一次,其它子应用加载时从缓存中获取

globalAssets 项配置,但这个配置不是很好,可以在 index.html 中给 link 标签和 script 标签添加 global 属性来标识

<link global rel="stylesheet" href="xx.css" />
<script global src="xx.js"></script>

子应用缓存,keep-alive 不会真正的被卸载,加载的生命周期是 afterHide beforeShow afterShow

丝滑转场

页面加载动效(或者骨架屏),基座应用加载完之后它消失,注意几个点 基座应用加载完成前端的 loading, micro-app 加载前后的 loading,及子应用渲染完成前后,还有点是每个子应用的独立个体的性能优化

优雅上线

这个已经不是我的学习重点了,过一下得了。如需要了解移步其它课程的学习

每一个应用单独打包部署,且集成到基座应用中,生部署子系统应用,然后再部署基座应用

发布工具

  • Putty
  • Xftp

服务器环境依赖

  • Nodejs 16.11.0
  • Nginx
  • Screen

线上域名

  • localhost
  • 线上域名
  • 📘 统一管理本地和线上 url

发布线上

🚩 子应用部署

  • Nuxt2 子应用
  • Vue2 子应用
  • Vue3 子应用
  • React18 子应用
  • Gzip 压缩

🟡 基座应用部署

  • Vue3 主应用

多页应用

mpa 不同与我们的微前端架构(单一技术架构),不能为了技术而技术,vue2 实现一个多页面应用(也不是这个的重点)

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4451

(0)
Walker的头像Walker
上一篇 2025年4月6日 22:23
下一篇 2025年4月27日 15:32

相关推荐

  • Nuxt3_扫盲 入门与原理介绍【学习笔记】

    Nuxt 3 入门与原理介绍 💡 什么是 Nuxt 3? Nuxt 3 是基于 Vue 3 和 Vite 打造的全栈前端框架,支持: 服务端渲染(SSR) 静态站点生成(SSG) 单页应用(SPA) 构建全栈应用(支持 API) Nuxt 3 是 Vue 的“加强版”,帮你简化项目结构和开发流程。 🔧 核心原理 功能 Nuxt 如何处理 ✅ 页面路由 自动根…

    个人 2025年4月6日
    42000
  • 测试专题

    个人 2025年2月7日
    40500
  • Go工程师体系课 protoc-gen-validate【学习笔记】

    protoc-gen-validate 简介与使用指南 ✅ 什么是 protoc-gen-validate protoc-gen-validate(简称 PGV)是一个 Protocol Buffers 插件,用于在生成的 Go 代码中添加结构体字段的验证逻辑。 它通过在 .proto 文件中添加 validate 规则,自动为每个字段生成验证代码,避免你手…

    个人 2025年4月27日
    20900
  • 无畏前行,拳释力量 🥊💪

    拼搏,是一种态度 生活就像一场比赛,没有捷径可走,只有不断训练、突破、超越,才能站上属于自己的舞台。这不仅是一种对抗,更是一种自我的觉醒——敢于迎战,敢于挑战,敢于成为更强的自己。 运动中的拼搏精神 无论是拳击、跑步,还是力量训练,每一次出拳、每一次挥汗、每一次咬牙坚持,都是对身体与心灵的磨炼。拼搏不是单纯的对抗,而是一种态度——面对挑战,不退缩;面对失败,…

    个人 2025年2月26日
    41600
  • 深入理解ES6 001【学习笔记】

    块级作用域绑定 之前的变量声明var无论在哪里声明的都被认为是作域顶部声明的,由于函数是一等公民,所以顺序一般是function 函数名()、var 变量。 块级声明 块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域存在于: 函数内部 块中(字符和{和}之间的区域) 临时死区 javascript引擎在扫描代码发现变量声明时,要么将它们提升至作…

    个人 2025年3月8日
    51100

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹