记录一次微前端改造:把 10+ 个独立 Vue 项目整合到一起

记录一次微前端改造:把 10+ 个独立 Vue 项目整合到一起

最近主导了公司前端项目的微前端化改造,踩了不少坑,也有一些收获。趁着记忆还新鲜,把过程记录下来,希望能给有类似需求的同学一些参考。

声明:我也是第一次做微前端,很多地方可能不够规范,欢迎大家指正。

一、背景:我们遇到了什么问题?

1.1 项目现状

公司的前端项目经过几年发展,变成了这样:

  • 10+ 个独立的 Vue 项目:认证系统、主业务平台、教育模块、社区模块、H5 移动端等
  • 每个项目都是独立的 Git 仓库,有自己的 CI/CD 流程,独立部署
  • 技术栈不太统一:有的是 Vue 2 + Vue CLI,有的是 Vue 3 + Vite
  • 用户体验比较割裂:在不同系统间跳转时,要重新加载整个页面

1.2 想解决的问题

问题 具体表现
体验不连贯 系统间跳转白屏时间长,感觉像在用不同的网站
登录状态 虽然用 Cookie 共享了 token,但体验还是不够好
重复建设 每个项目都有一套类似的基础代码

说实话,一开始我对微前端也没什么概念,是领导提出想把这些系统整合成一个统一入口,我才开始研究这个方向。

二、技术选型:为什么选 micro-app?

2.1 调研过的方案

方案 我的理解 为什么没选/选了
iframe 最简单,天然隔离 体验不好,通信麻烦
qiankun 最成熟,基于 single-spa 配置比较复杂,子应用改造成本高
micro-app 京东出的,基于 Web Components 接入简单,改造成本低 ✅
Module Federation Webpack 5 的功能 我们有的项目还在用 Vue CLI 4

2.2 选择 micro-app 的原因

说实话,主要是因为简单

看了官方文档后,发现子应用几乎不需要怎么改,基座应用也就几行代码:

javascript 复制代码
// 基座应用启动
import microApp from '@micro-zoe/micro-app'
microApp.start()

// 加载子应用就一个标签
<micro-app name="app1" url="http://localhost:8081/"></micro-app>

对于我这种微前端新手来说,能快速跑起来比什么都重要。

三、项目结构

3.1 实际情况

需要说明的是,我们的项目结构是这样的:

复制代码
# 这些都是独立的 Git 仓库,我只是在本地建了个文件夹把它们放一起方便开发
project/           # 本地文件夹(不是 Git 仓库)
├── project-base/            # 基座应用 - 独立 Git 仓库
├── project-main/            # 主业务 - 独立 Git 仓库
├── project-auth/            # 认证系统 - 独立 Git 仓库
├── project-edu/             # 教育模块 - 独立 Git 仓库
├── project-community/       # 社区模块 - 独立 Git 仓库
└── ...

每个项目都是独立部署的,有自己的 Docker 镜像和部署流程。微前端改造并没有改变这一点,只是加了一个基座应用来统一加载它们。

3.2 端口规划

本地开发时,我给每个应用分配了不同的端口:

应用 端口 路由前缀
project-base 8080 /
project-main 8081 /main
project-auth 8082 /auth
project-community 8083 /communities
project-edu 8085 /edu

四、具体怎么做的

4.1 基座应用

基座应用是新建的,用的 Vite + Vue 3。

1. 安装和初始化 micro-app

typescript 复制代码
// project-base/src/main.ts
import microApp from "@micro-zoe/micro-app";

microApp.start({
  plugins: {
    modules: {},
  },
});

2. Vite 需要配置一下,不然会报错

typescript 复制代码
// project-base/vite.config.ts
vue({
  template: {
    compilerOptions: {
      // 告诉 Vue:micro-app 是自定义元素,别当组件解析
      isCustomElement: (tag) => /^micro-app/.test(tag),
    },
  },
});

3. 路由配置

typescript 复制代码
// 用通配符匹配子应用的所有路由
const routes = [
  {
    path: "/main/:page*",
    component: () => import("@/views/main.vue"),
  },
  {
    path: "/auth/:page*",
    component: () => import("@/views/auth.vue"),
  },
  // ...
];

4. 子应用挂载组件

vue 复制代码
<!-- project-base/src/views/main.vue -->
<template>
  <micro-app name="project-main" :url="url" baseroute="/main" />
</template>

<script setup>
const url = "http://localhost:8081/child/main/";
</script>

4.2 子应用改造

子应用的改造确实不多,主要是这几步:

1. 新建 public-path.ts

typescript 复制代码
// src/public-path.ts
if (window.__MICRO_APP_ENVIRONMENT__) {
  __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__;
}

2. 改造入口文件

typescript 复制代码
// src/main.ts
import "./public-path"; // 必须放最前面

let app = null;
let router = null;
let history = null;

function mount() {
  history = createWebHistory(
    window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL
  );
  router = createRouter({ history, routes });
  app = createApp(App);
  app.use(router);
  app.mount("#app");
}

function unmount() {
  app?.unmount();
  history?.destroy();
  app = null;
  router = null;
  history = null;
}

// 判断运行环境
if (window.__MICRO_APP_ENVIRONMENT__) {
  // 微前端环境
  window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount };
} else {
  // 独立运行
  mount();
}

3. 配置跨域(开发环境)

javascript 复制代码
// vue.config.js
module.exports = {
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
};

4.3 状态共享

这块我承认做得不太规范。

按理说应该用 micro-app 提供的通信机制,但我们之前的项目已经有一套基于 localStorage + Cookie 的方案在用了,而且子应用还需要支持独立运行,所以就没改。

typescript 复制代码
// 公共组件库里的 store
export default {
  set(key, data) {
    localStorage.setItem(key, JSON.stringify({ data }));
  },
  get(key) {
    const cache = localStorage.getItem(key);
    return cache ? JSON.parse(cache).data : null;
  },
  // token 用 cookie 存,这样可以跨子域共享
  set_cookie(name, value) {
    document.cookie = `${name}=${value}; path=/; max-age=${30 * 24 * 60 * 60}`;
  },
};

这个方案的问题

  • 不够实时,一个应用改了数据,另一个应用需要刷新才能看到
  • 没有利用 micro-app 的能力

但也有好处

  • 简单,团队都能理解
  • 子应用可以独立运行
  • 刷新页面不丢数据

后续有时间可能会改成用 micro-app 的 setGlobalData,但目前这样也能用。

五、部署

每个应用还是独立部署,只是 Nginx 配置需要调整一下。

基座应用

nginx 复制代码
server {
    listen 80;

    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }
}

子应用

nginx 复制代码
server {
    listen 80;

    location /child/main {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /child/main/index.html;
    }
}

六、踩过的坑

6.1 静态资源 404

子应用的图片加载不出来,因为路径变了。

解决 :确保 public-path.ts 配置正确,而且要放在入口文件最前面 import。

6.2 路由跳转 URL 不变

子应用内部跳转后,浏览器地址栏没变化。

解决 :路由的 base 要用 window.__MICRO_APP_BASE_ROUTE__

6.3 热更新有问题

开发时子应用热更新会导致页面白屏。

解决 :子应用关闭热更新 hot: false。有点麻烦,但暂时没找到更好的办法。

6.4 样式偶尔会串

虽然 micro-app 有样式隔离,但有些全局样式还是会影响。

解决:尽量用 scoped 样式,避免写太通用的选择器。

七、目前的效果

改造完成后:

  • ✅ 用户可以在一个页面里切换不同的系统,不用重新加载
  • ✅ 各个子应用还是可以独立开发、独立部署
  • ✅ 子应用也可以单独访问(方便开发调试)

还存在的问题:

  • ⚠️ 状态共享方案不够优雅
  • ⚠️ 没有用到 micro-app 的很多高级功能(预加载、keep-alive 等)
  • ⚠️ 首次加载子应用还是有点慢

八、后续想做的

  1. 研究一下预加载:micro-app 支持 preFetch,应该能提升切换速度
  2. 优化状态共享:看看能不能用 micro-app 的全局数据机制
  3. 统一技术栈:慢慢把 Vue 2 的项目升级到 Vue 3

九、一些想法

做完这次改造,有几点感受:

  1. 微前端不是银弹。如果项目本身就不大,或者团队就几个人,可能真没必要搞这么复杂。

  2. 先跑起来再说。一开始不要想着做到完美,能用就行,后面再慢慢优化。

  3. 保持子应用的独立性。我觉得这点很重要,子应用能独立运行,开发调试都方便很多。

  4. 文档很重要。我们内部写了一份接入文档,新同事照着做基本都能跑起来。

十、参考资料


以上就是这次微前端改造的记录。如有错误或者更好的方案,欢迎评论区交流~

相关推荐
程序员小寒2 小时前
前端高频面试题之Promise相关方法
前端·javascript·面试
IT_陈寒2 小时前
JavaScript 开发者必知的 7 个 ES2023 新特性,第5个能让代码量减少50%
前端·人工智能·后端
李少兄2 小时前
前端开发中的 CSS @keyframes 动画指南
前端·css
LYFlied2 小时前
前端技术风险防控:以防为主,防控结合
前端·工程化·技术风险防控
小圣贤君2 小时前
从零到一:打造专业级小说地图设计工具的技术实践
vue.js·electron·写作·canvas·小说·小说地图
阿蒙Amon2 小时前
JavaScript学习笔记:8.日期和时间
javascript·笔记·学习
宁雨桥2 小时前
前端跨页面通信:从基础到工程化的全面指南
前端·vue.js·react.js
梵尔纳多2 小时前
electron 安装
前端·javascript·electron