Xây dựng kiến trúc Micro-Frontend trong 15 phút với Vite
Bài viết hướng dẫn cách thiết lập một kiến trúc micro-frontend sử dụng Vite làm công cụ build và kết hợp các component Vue.js, Angular và React thành một trải nghiệm thống nhất. Ví dụ minh họa là một cổng thông tin tin tức, nơi mỗi framework xử lý một phần cụ thể.
Trong kiến trúc frontend nguyên khối (monolithic), một codebase duy nhất xử lý toàn bộ giao diện người dùng. Mặc dù điều này có thể đơn giản hóa việc phát triển ban đầu, nhưng nó có thể trở nên phức tạp khi ứng dụng phát triển. Việc mở rộng quy mô gặp khó khăn khi các nhóm lớn làm việc trong một repo nguyên khối có thể gặp phải xung đột merge, pipeline CI/CD chậm hơn và khó khăn với các dependency. Tính độc lập cũng bị ảnh hưởng vì đôi khi làm việc trên các phần được chia sẻ có thể tác động đến các nhóm khác. Và cuối cùng, khả năng phục hồi kém, một lỗi có thể làm sập toàn bộ ứng dụng.
Micro-frontend có thể giúp ích khi bạn bắt đầu gặp phải những vấn đề đó. Không giống như Web Components, đi kèm với giao tiếp giữa các framework bị hạn chế và những thách thức trong việc quản lý vòng đời, micro-frontend dựa trên Vite cho phép các nhà phát triển làm việc với các framework khác nhau. Chúng cung cấp tính linh hoạt trong việc sử dụng công cụ, quản lý trạng thái tốt hơn và các tùy chọn tích hợp mạnh mẽ hơn. Trong một phần mềm thực tế phát triển qua nhiều năm, việc có thể xử lý nhiều framework có thể là một cách trơn tru để chuyển từ framework cũ sang framework mới bất cứ khi nào cần thiết.
Trong bài viết này, chúng ta sẽ tạo một thiết lập micro-frontend bằng cách sử dụng Vite làm công cụ build và kết hợp các component Vue.js, Angular và React thành một trải nghiệm thống nhất. Ví dụ? Một cổng thông tin tin tức dạng module, nơi mỗi framework xử lý một phần cụ thể.
Cổng thông tin tin tức dạng module
Cổng thông tin tin tức dạng module này sẽ có:
- Header được tạo bằng Vue.js với một thanh điều hướng đơn giản.
- Trending được tạo bằng React hiển thị các bài viết mới nhất.
- Highlights được tạo bằng Angular với các bài viết phổ biến nhất.
Trong một ví dụ thực tế, việc tách biệt việc quản lý tin tức trên nhiều công nghệ sẽ không lý tưởng, nhưng nó phục vụ tốt cho ví dụ của chúng ta
Xây dựng Shell
Trong kiến trúc micro-frontend, Shell hoạt động như một container cho các micro-frontend. Nó có 3 tính năng chính:
- Điều phối: Shell chịu trách nhiệm tải và render các micro-frontend khác nhau vào các khu vực được chỉ định của ứng dụng.
- Sắp xếp: Nó xử lý các mối quan tâm chung như điều hướng, kiểu dáng được chia sẻ, trạng thái được chia sẻ, dữ liệu...
- Điểm vào: Đây là thứ người dùng tải đầu tiên trong trình duyệt của họ. Nó cũng có thể cung cấp các phương án dự phòng nếu một trong các micro-frontend không tải được.
Shell trong thiết lập của chúng ta sẽ sử dụng Vite và tải các module ESM một cách linh hoạt. Cấu trúc thư mục như sau:
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/
Chúng ta bắt đầu bằng cách khởi tạo một workspace Vite cho host và các micro-frontend.
mkdir news-portal && cd news-portal
npm init vite@latest host --template vanilla
Sau đó, sắp xếp dự án để tách biệt từng micro-frontend:
mkdir -p apps/header apps/trending apps/highlights
Tiếp theo, tạo file index.html đơn giản với kiến trúc các component bên trong DOM:
<!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>
Bây giờ, hãy tạo file main.js chịu trách nhiệm gắn kết (mount) các micro-frontend (lưu ý rằng việc import sẽ không hoạt động cho đến khi chúng ta build các micro-frontend):
/ 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'));
Sau đó, tạo cấu hình Vite bên trong vite.config.js để kích hoạt Vite server:
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 3000,
open: true,
},
});
Khởi động Shell để sẵn sàng phục vụ:
cd host
npm install
npm run dev
Và đây rồi: chúng ta đã tạo thành công Shell và nó đã sẵn sàng để phục vụ các micro-frontend trong tương lai. Bây giờ, hãy tạo chúng!
Xây dựng Header với Vue 3
Hãy tạo thư mục Header bên trong apps và điều hướng vào đó:
cd apps
npm init vite@latest header --template vue
cd header
npm install
Bên trong src/components/Header.vue, tạo một header đơn giản với điều hướng và ví dụ như một thanh tìm kiếm:
<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>
Chúng ta cần một file src/main.js để gắn kết (mount) component:
import { createApp } from 'vue';
import Header from './components/Header.vue';
export function mount(el) {
createApp(Header).mount(el);
}
Cấu hình vite.config.js để hiển thị ứng dụng này dưới dạng một thư viện:
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',
},
},
})
Cuối cùng, build micro-frontend để tạo thư mục dist:
cd apps/header
npm run build
Bạn sẽ thấy Header được hiển thị trong Shell. Điều này xảy ra vì chúng ta đã yêu cầu Shell phục vụ thư mục dist của Header và chúng ta đã tạo nó bằng lệnh npm run build.
Xây dựng phần Trending với React 18
Tạo thư mục Trending bên trong apps và điều hướng vào đó:
cd apps
npm init vite@latest trending --template react
cd trending
npm install
Thêm component Trending vào 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;
Chúng ta cần một file src/main.jsx để gắn kết (mount) component:
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 />);
}
Cấu hình vite.config.js:
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: './src/main.jsx',
name: 'Trending',
fileName: 'trending',
},
},
});
Build để tạo thư mục dist:
npm run build
Và chúng ta đã có micro-frontend thứ hai được hiển thị bên trong Shell, dưới Header.
Những cạm bẫy thường gặp
- Xung đột cổng: Gán một cổng duy nhất cho mỗi micro-frontend trong vite.config.js hoặc angular.json để tránh xung đột.
- Thư viện dùng chung: Sử dụng CDN chung cho các thư viện dùng chung để giảm trùng lặp và kích thước bundle. Đảm bảo tính tương thích phiên bản để tránh lỗi runtime.
Vấn đề CORS
Trong quá trình phát triển cục bộ, các micro-frontend được lưu trữ trên các cổng khác nhau có thể gặp sự cố CORS do chính sách bảo mật của trình duyệt.
Đối với production, hãy cấu hình máy chủ web của bạn để cho phép các yêu cầu cross-origin:
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';
Giảm thiểu các yêu cầu CORS bằng cách triển khai tất cả các micro-frontend và Shell dưới cùng một domain hoặc cấu trúc subdomain (ví dụ: https://news-portal.com/header, https://news-portal.com/trending).
Kết luận
Xin chúc mừng, bạn đã thiết lập kiến trúc Micro-Frontend đầu tiên của mình với Vite. Đối với những người đã sử dụng web components trước đây, bạn có thể thấy nó thực sự đơn giản và hữu ích hơn. Tôi hy vọng nó sẽ là một trợ giúp tốt để thiết lập các dự án MFE đầu tiên của bạn bất cứ khi nào bạn cần tách rời việc phát triển nhiều phần front của phần mềm.
All rights reserved