使用 Vite 在 15 分钟内设置微前端架构!
在单体式前端架构中,单个代码库负责处理整个用户界面。虽然这可以简化初始开发,但随着应用程序的增长,它会变得复杂:
- 扩展:在单体仓库中工作的大型团队可能会面临合并冲突、较慢的 CI/CD 管道以及依赖关系困难
- 独立性:有时处理共享内容可能会影响其他团队
- 弹性:一次故障可能会导致整个应用程序崩溃
当你开始面临这些问题时,微前端可以帮到你。与 Web 组件不同,Web 组件的跨框架通信受限,并且生命周期管理也面临挑战,而基于 Vite 的微前端允许开发者使用不同的框架。它们提供了灵活的工具、更完善的状态管理和更强大的集成选项。在一个经过多年发展的实际软件中,能够处理多个框架可以让你在需要时顺利地从旧框架迁移到新框架。
在本文中,我们将使用Vite作为构建工具,创建一个微前端环境,并将Vue.js、Angular和React组件整合到统一的体验中。例如,一个模块化新闻门户,每个框架负责处理特定的部分。
模块化新闻门户
这个模块化新闻门户将具有:
- 使用 Vue.js 制作的Header:带有一个简单的导航栏
- 使用 React 制作的 Trending:显示最后一篇文章
- 使用 Angular 制作的精彩内容:展示最受欢迎的文章
在现实世界的例子中,将新闻管理分散到多种技术中并不是理想的选择,但它很好地服务于我们的例子。
构建 Shell
在微前端架构中,Shell充当微前端的容器。它有 3 个主要特点:
- 协调:Shell 负责将不同的微前端加载并渲染到应用程序的指定区域
- 编排:它处理导航、共享样式、共享状态、数据等全局问题……
- 入口点:这是用户在浏览器中首先加载的内容。如果某个微前端加载失败,它也可以提供回退机制。
我们设置中的 Shell 将使用 Vite 并动态加载 ESM 模块:
host/
├── index.html # Main entry point
├── main.js # Loads the micro-frontends
├── vite.config.js
├── package.lock.json
├── package.json
apps/ # Contains individual micro-frontends
├── header/
├── src/
├── components/
├── Header.vue
├── main.js
├── vite.config.js
├── package.lock.json
├── package.json
├── trending/
├── src/
├── components
├── Trending.jsx
├── main.jsx
├── eslint.config.js
├── package.lock.json
├── vite.config.js
├── highlights/
让我们建造它吧!
我们首先为我们的主机和微前端初始化一个 Vite 工作区。
mkdir news-portal && cd news-portal
npm init vite@latest host --template vanilla
让我们组织项目来分离每个微前端:
mkdir -p apps/header apps/trending apps/highlights
现在,让我们使用 DOM 中的组件架构创建简单的 index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News Portal</title>
</head>
<body>
<div id="header"></div>
<div id="trending"></div>
<div id="highlights"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>
现在,让我们创建负责挂载微前端的 main.js 文件(请注意,在我们构建微前端之前,导入不会起作用):
// main.js
import { mount as mountHeader } from '../apps/header/dist/header.js';
import { mount as mountTrending } from '../apps/trending/dist/trending.js';
import { mount as mountHighlights } from '../apps/highlights/dist/highlights.js';
mountHeader(document.querySelector('#header'));
mountTrending(document.querySelector('#trending'));
mountHighlights(document.querySelector('#highlights'));
然后,我们在 vite.config.js 中创建 Vite 配置以启用 Vite 服务器:
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 3000,
open: true,
},
});
启动 Shell 以准备服务:
cd host
npm install
npm run dev
现在,我们已经成功创建了 Shell,它已经准备好为我们未来的微前端提供服务了。现在,让我们开始创建它们吧!
使用 Vue 3 构建标题
让我们在应用程序中创建Header文件夹并导航到其中:
cd apps
npm init vite@latest header --template vue
cd header
npm install
在里面src/components/Header.vue
,创建一个带有导航和搜索栏的简单标题:
<template>
<header class="header">
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">World</a></li>
<li><a href="#">Tech</a></li>
<li><a href="#">Sports</a></li>
</ul>
</nav>
<input type="text" placeholder="Search news..." />
</header>
</template>
<script>
export default {
name: 'Header',
};
</script>
<style scoped>
.header {
display: flex;
justify-content: space-between;
padding: 1em;
background: #333;
color: white;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-right: 1em;
}
input {
padding: 0.5em;
}
</style>
我们需要一个src/main.js
来安装组件:
import { createApp } from 'vue';
import Header from './components/Header.vue';
export function mount(el) {
createApp(Header).mount(el);
}
配置 vite.config.js 以将此应用程序公开为库:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: './src/main.js',
name: 'Header',
fileName: 'header',
},
},
})
最后,构建微前端以生成 dist 文件夹:
cd apps/header
npm run build
现在你应该能够在Shell中看到Header的服务了。这是因为我们告诉 Shell 要使用 Header 的 dist 文件夹,并且我们用命令生成了它。npm build
使用 React 18 构建趋势部分
让我们在应用程序内创建Trending文件夹并导航到其中:
cd apps
npm init vite@latest trending --template react
cd trending
npm install
添加趋势组件src/components/Trending.jsx
import React from 'react';
const Trending = () => {
const articles = [
{ id: 1, title: "React 18 Released", summary: "Learn what's new in React 18." },
{ id: 2, title: "AI Revolution", summary: "How AI is transforming industries." },
];
return (
<section className="trending">
<h2>Trending News</h2>
<ul>
{articles.map((article) => (
<li key={article.id}>
<h3>{article.title}</h3>
<p>{article.summary}</p>
</li>
))}
</ul>
</section>
);
};
export default Trending;
我们需要一个src/main.jsx
来安装组件:
import React from 'react';
import ReactDOM from 'react-dom/client';
import Trending from './components/Trending';
export function mount(el) {
const root = ReactDOM.createRoot(el);
root.render(<Trending />);
}
配置vite.config.js
:
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: './src/main.jsx',
name: 'Trending',
fileName: 'trending',
},
},
});
构建以生成 dist 文件夹:
npm run build
好了!现在,我们的 Shell 内部、Header 下已经有了第二个微前端。
使用 Angular 19 构建亮点
补充:如果没有模块联合、自定义元素或小部件,我暂时无法让 Vite 与 Angular 19 兼容。我目前正在尝试在这三者之间寻找最佳方案,以便在本文后续的编辑中向您推荐最高效的方案。
奖励:与 Vite 的运行时联合
虽然上述方法侧重于在构建步骤中组合微前端,但对于生产环境,您可能需要采用运行时联合。这种方法为微前端提供了真正的独立性,允许每个模块单独部署和更新。
每个微前端都可以托管在自己的服务器或域上。例如:
Shell应用程序中的动态加载
Shell 应用程序在运行时使用 import() 或类似方法动态加载微前端。以下是 Shell 如何从 Shell 中微前端清单 json 文档中列出的相应 URL 动态加载 Header 和 Trending 模块main.js
:
微服务清单(例如: https: //news-portal.com/microfrontends.json):
[
{
"name": "Header",
"url": "https://header.news-portal.com/dist/header.js"
},
{
"name": "Trending",
"url": "https://trending.news-portal.com/dist/trending.js"
}
]
壳main.js
:
export default {
data() {
return {
modules: [],
};
},
async mounted() {
try {
const response = await fetch('https://news-portal.com/microfrontends.json');
const microfrontends = await response.json();
for (const mfe of microfrontends) {
const module = await import(mfe.url);
this.modules.push({
name: mfe.name,
component: module.default,
});
}
} catch (error) {
console.error('Failed to load microfrontends:', error);
}
},
template: `
<div>
<h1>Shell App</h1>
<div v-for="module in modules" :key="module.name">
<component :is="module.component" />
</div>
</div>
`,
};
共享依赖项
为了避免重复使用 Vue 等常见依赖项,我们可以在 Vite 构建过程中使用peerDependencies
并package.json
外部化它们。我们可以在 中进行配置vite.config.js
:
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
external: ['vue'], // Prevent bundling Vue with the module
},
},
});
这确保Shell提供依赖项(例如 Vue),并且Header和Trending模块都依赖于同一个实例。
运行时联合的好处
- 独立部署:每个模块都可以更新,而无需重新部署整个应用程序。
- 可扩展性:团队可以自主地在不同的微前端上工作。
- 动态组合:无需触及 shell 或其他微前端即可添加或更新新模块。
常见的陷阱
端口冲突
- 在 vite.config.js 或 angular.json 中为每个微前端分配一个唯一的端口以避免冲突
共享库
- 对常用库使用共享 CDN,以减少重复和捆绑包大小
- 确保版本兼容性以避免运行时错误
CORS问题
在本地开发过程中,由于浏览器安全策略的原因,托管在不同端口上的微前端可能会面临 CORS 问题
对于生产环境,请配置您的 Web 服务器以允许跨源请求:
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept';
通过将所有微前端和 Shell 部署在同一域或子域结构下(例如,https://news-portal.com/header、https : //news-portal.com/trending),最大限度地减少 CORS 要求。
Github链接
Github 代码:代码库
结论
恭喜,您已经使用 Vite 搭建了第一个 MicroFrontend 架构。对于之前使用过 Web Components 的用户,您可能会发现它更加简单易用。希望它能够在您需要解耦软件多个前端开发时,帮助您搭建第一个 MFE 项目。
文章来源:https://dev.to/onepoint/setup-a-micro-frontend-architecture-in-15min-with-vite-4pbg