Tiếng Việt

Khai phá sức mạnh của micro-frontends với JavaScript Module Federation trong Webpack 5. Tìm hiểu cách xây dựng các ứng dụng web có khả năng mở rộng, dễ bảo trì và độc lập.

JavaScript Module Federation với Webpack 5: Hướng Dẫn Toàn Diện về Micro-frontends

Trong bối cảnh không ngừng phát triển của ngành phát triển web, việc xây dựng các ứng dụng lớn và phức tạp có thể là một nhiệm vụ khó khăn. Các kiến trúc nguyên khối truyền thống thường dẫn đến thời gian phát triển tăng, tắc nghẽn trong quá trình triển khai, và những thách thức trong việc duy trì chất lượng mã nguồn. Micro-frontends đã nổi lên như một mẫu kiến trúc mạnh mẽ để giải quyết những thách thức này, cho phép các nhóm xây dựng và triển khai các phần độc lập của một ứng dụng web lớn hơn. Một trong những công nghệ hứa hẹn nhất để triển khai micro-frontends là JavaScript Module Federation, được giới thiệu trong Webpack 5.

Micro-frontends là gì?

Micro-frontends là một phong cách kiến trúc trong đó một ứng dụng frontend được phân tách thành các đơn vị nhỏ hơn, độc lập, có thể được phát triển, kiểm thử và triển khai một cách tự chủ bởi các nhóm khác nhau. Mỗi micro-frontend chịu trách nhiệm cho một lĩnh vực kinh doanh hoặc tính năng cụ thể, và chúng được kết hợp lại với nhau vào thời điểm chạy để tạo thành giao diện người dùng hoàn chỉnh.

Hãy nghĩ về nó như một công ty: thay vì có một đội ngũ phát triển khổng lồ, bạn có nhiều đội nhỏ hơn tập trung vào các lĩnh vực cụ thể. Mỗi đội có thể làm việc độc lập, cho phép chu kỳ phát triển nhanh hơn và bảo trì dễ dàng hơn. Hãy xem xét một nền tảng thương mại điện tử lớn như Amazon; các đội khác nhau có thể quản lý danh mục sản phẩm, giỏ hàng, quy trình thanh toán và quản lý tài khoản người dùng. Tất cả những thứ này có thể là các micro-frontend độc lập.

Lợi ích của Micro-frontends:

Thách thức của Micro-frontends:

JavaScript Module Federation là gì?

JavaScript Module Federation là một tính năng của Webpack 5 cho phép bạn chia sẻ mã nguồn giữa các ứng dụng JavaScript được biên dịch riêng biệt tại thời điểm chạy. Nó cho phép bạn phơi bày các phần của ứng dụng của mình dưới dạng "modules" mà các ứng dụng khác có thể sử dụng, mà không cần phải xuất bản lên một kho lưu trữ trung tâm như npm.

Hãy coi Module Federation như một cách để tạo ra một hệ sinh thái liên kết các ứng dụng, nơi mỗi ứng dụng có thể đóng góp chức năng của riêng mình và sử dụng chức năng từ các ứng dụng khác. Điều này loại bỏ sự cần thiết của các phụ thuộc tại thời điểm xây dựng và cho phép triển khai thực sự độc lập.

Ví dụ, một nhóm hệ thống thiết kế có thể phơi bày các thành phần UI dưới dạng modules, và các nhóm ứng dụng khác nhau có thể sử dụng các thành phần này trực tiếp từ ứng dụng hệ thống thiết kế, mà không cần phải cài đặt chúng dưới dạng gói npm. Khi nhóm hệ thống thiết kế cập nhật các thành phần, các thay đổi sẽ tự động được phản ánh trong tất cả các ứng dụng đang sử dụng.

Các khái niệm chính trong Module Federation:

Thiết lập Module Federation với Webpack 5: Hướng dẫn thực hành

Chúng ta hãy cùng xem một ví dụ thực tế về việc thiết lập Module Federation với Webpack 5. Chúng ta sẽ tạo hai ứng dụng đơn giản: một ứng dụng Host (chủ) và một ứng dụng Remote (từ xa). Ứng dụng Remote sẽ phơi bày một component, và ứng dụng Host sẽ sử dụng nó.

1. Thiết lập dự án

Tạo hai thư mục riêng biệt cho các ứng dụng của bạn: `host` và `remote`.

```bash mkdir host remote cd host npm init -y npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev npm install react react-dom cd ../remote npm init -y npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev npm install react react-dom ```

2. Cấu hình ứng dụng Remote

Trong thư mục `remote`, tạo các tệp sau:

src/index.js:

```javascript import React from 'react'; import ReactDOM from 'react-dom/client'; import RemoteComponent from './RemoteComponent'; const App = () => (

Remote Application

); const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); ```

src/RemoteComponent.jsx:

```javascript import React from 'react'; const RemoteComponent = () => (

This is a Remote Component!

Rendered from the Remote Application.

); export default RemoteComponent; ```

webpack.config.js:

```javascript const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const path = require('path'); module.exports = { entry: './src/index', mode: 'development', devServer: { port: 3001, static: { directory: path.join(__dirname, 'dist'), }, }, output: { publicPath: 'auto', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'], }, }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'remote', filename: 'remoteEntry.js', exposes: { './RemoteComponent': './src/RemoteComponent', }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], resolve: { extensions: ['.js', '.jsx'], }, }; ```

Tạo `public/index.html` với cấu trúc HTML cơ bản. Quan trọng là có `

`

3. Cấu hình ứng dụng Host

Trong thư mục `host`, tạo các tệp sau:

  • `src/index.js`: Điểm vào của ứng dụng.
  • `webpack.config.js`: Tệp cấu hình Webpack.

src/index.js:

```javascript import React, { Suspense } from 'react'; import ReactDOM from 'react-dom/client'; const RemoteComponent = React.lazy(() => import('remote/RemoteComponent')); const App = () => (

Host Application

Loading Remote Component...
}>
); const root = ReactDOM.createRoot(document.getElementById('root')); root.render(); ```

webpack.config.js:

```javascript const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const path = require('path'); module.exports = { entry: './src/index', mode: 'development', devServer: { port: 3000, static: { directory: path.join(__dirname, 'dist'), }, }, output: { publicPath: 'auto', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'], }, }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'host', remotes: { remote: 'remote@http://localhost:3001/remoteEntry.js', }, shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true }, }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], resolve: { extensions: ['.js', '.jsx'], }, }; ```

Tạo `public/index.html` với cấu trúc HTML cơ bản (tương tự như ứng dụng remote). Quan trọng là có `

`

4. Cài đặt Babel

Trong cả hai thư mục `host` và `remote`, cài đặt các phụ thuộc của Babel:

```bash npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader ```

5. Chạy các ứng dụng

Trong cả hai thư mục `host` và `remote`, thêm script sau vào `package.json`:

```json "scripts": { "start": "webpack serve" } ```

Bây giờ, hãy khởi động cả hai ứng dụng:

```bash cd remote npm start cd ../host npm start ```

Mở trình duyệt của bạn và truy cập `http://localhost:3000`. Bạn sẽ thấy ứng dụng Host với Remote Component được hiển thị bên trong nó.

Giải thích các tùy chọn cấu hình chính:

Các kỹ thuật Module Federation nâng cao

Module Federation cung cấp nhiều tính năng nâng cao có thể giúp bạn xây dựng các kiến trúc micro-frontend phức tạp hơn nữa.

Remotes động

Thay vì mã hóa cứng các URL của các ứng dụng từ xa trong cấu hình Webpack, bạn có thể tải chúng một cách động tại thời điểm chạy. Điều này cho phép bạn dễ dàng cập nhật vị trí của các ứng dụng từ xa mà không cần phải xây dựng lại ứng dụng chủ.

Ví dụ, bạn có thể lưu trữ các URL của các ứng dụng từ xa trong một tệp cấu hình hoặc một cơ sở dữ liệu và tải chúng một cách động bằng JavaScript.

```javascript // Trong webpack.config.js remotes: { remote: `promise new Promise(resolve => { const urlParams = new URLSearchParams(window.location.search); const remoteUrl = urlParams.get('remote'); // Giả sử remoteUrl là 'http://localhost:3001/remoteEntry.js' const script = document.createElement('script'); script.src = remoteUrl; script.onload = () => { // chìa khóa của module federation là ứng dụng remote // có sẵn bằng cách sử dụng tên trong remote resolve(window.remote); }; document.head.appendChild(script); })`, }, ```

Bây giờ bạn có thể tải ứng dụng chủ với một tham số truy vấn `?remote=http://localhost:3001/remoteEntry.js`

Phiên bản hóa Module được chia sẻ

Module Federation có thể tự động xử lý việc quản lý phiên bản và loại bỏ trùng lặp các module được chia sẻ để đảm bảo rằng chỉ có một phiên bản tương thích của mỗi module được tải. Điều này đặc biệt quan trọng khi làm việc với các ứng dụng lớn và phức tạp có nhiều phụ thuộc.

Bạn có thể chỉ định phạm vi phiên bản của mỗi module được chia sẻ trong cấu hình Webpack.

```javascript // Trong webpack.config.js shared: { react: { singleton: true, eager: true, requiredVersion: '^18.0.0' }, 'react-dom': { singleton: true, eager: true, requiredVersion: '^18.0.0' }, }, ```

Trình tải module tùy chỉnh

Module Federation cho phép bạn định nghĩa các trình tải module tùy chỉnh có thể được sử dụng để tải các module từ các nguồn khác nhau hoặc ở các định dạng khác nhau. Điều này có thể hữu ích để tải các module từ một CDN hoặc từ một registry module tùy chỉnh.

Chia sẻ trạng thái giữa các Micro-frontend

Một trong những thách thức của kiến trúc micro-frontend là chia sẻ trạng thái giữa các micro-frontend khác nhau. Có một số cách tiếp cận bạn có thể thực hiện để giải quyết thách thức này:

Các phương pháp hay nhất để triển khai Micro-frontends với Module Federation

Dưới đây là một số phương pháp hay nhất cần ghi nhớ khi triển khai micro-frontends với Module Federation:

Ví dụ thực tế về Module Federation

Mặc dù các nghiên cứu tình huống cụ thể thường được giữ bí mật, đây là một số kịch bản tổng quát mà Module Federation có thể cực kỳ hữu ích:

Kết luận

JavaScript Module Federation trong Webpack 5 cung cấp một cách mạnh mẽ và linh hoạt để xây dựng kiến trúc micro-frontend. Nó cho phép bạn chia sẻ mã nguồn giữa các ứng dụng JavaScript được biên dịch riêng biệt tại thời điểm chạy, cho phép triển khai độc lập, đa dạng công nghệ và cải thiện quyền tự chủ của nhóm. Bằng cách tuân theo các phương pháp hay nhất được nêu trong hướng dẫn này, bạn có thể tận dụng Module Federation để xây dựng các ứng dụng web có khả năng mở rộng, dễ bảo trì và sáng tạo.

Tương lai của phát triển frontend chắc chắn đang nghiêng về các kiến trúc mô-đun và phân tán. Module Federation cung cấp một công cụ quan trọng để xây dựng các hệ thống hiện đại này, cho phép các nhóm tạo ra các ứng dụng phức tạp với tốc độ, tính linh hoạt và khả năng phục hồi cao hơn. Khi công nghệ này trưởng thành, chúng ta có thể mong đợi sẽ thấy nhiều trường hợp sử dụng sáng tạo và các phương pháp hay nhất xuất hiện hơn nữa.