Micro frontend tại sao và như thế nào?

Công Nghệ
Micro frontend tại sao và như thế nào?
Bài viết được sự cho phép của tác giả Lưu Bình An Tại sao bạn cần biết đến Micro frontend Vấn đề cần giải quyết: Ứng dụng càng lúc càng phình ra về quy mô, cũng như độ phức tạp Một codebase FE duy nhất mà muốn maintain thì chỉ...

Bài viết được sự cho phép của tác giả Lưu Bình An

Tại sao bạn cần biết đến Micro frontend

Vấn đề cần giải quyết:

  • Ứng dụng càng lúc càng phình ra về quy mô, cũng như độ phức tạp
  • Một codebase FE duy nhất mà muốn maintain thì chỉ có gặp ác mộng hằng đêm
  • Bạn có nhiều team FE khác nhau, mỗi team chỉ làm việc chính trên một phần tính năng nào đó rất cụ thể, chỉ 1 codebase mà hơn 5 team vào làm việc trên đó thì thôi xong
  • Bạn muốn có 1 codebase viết bằng typescript, một codebase viết js, một feature được build bằng React, feature khác được build Vue

Micro frontend là cái gì

Đây là cách tiếp cận cũng na ná như microservice, thay vì 1, chúng ta có nhiều codebase, và trên từng codebase chỉ quản lý một tính năng cụ thể mà thôi.

Có thể xem một ứng dụng web là một bộ kết hợp của nhiều tính năng, mỗi một tính năng như vậy được quản lý bởi một team

Micro frontend tại sao và như thế nào?Micro frontend tại sao và như thế nào?

Thuật ngữ này được giới thiệu lần đầu vào 2016 bởi Thourghtworks Tech Radar

An architectural style where independently deliverable frontend applications are composed into a greater whole

Micro frontend tại sao và như thế nào?Micro frontend tại sao và như thế nào?

Một cách trực quan hơn bạn có thể tham khảo hình sau

Micro frontend tại sao và như thế nào?Micro frontend tại sao và như thế nào?

Còn đây là demo của trang microfrontends.com https://demo.microfrontends.com/

Nhiều công ty tuyển dụng Frontend Developer đãi ngộ tốt, ứng tuyển ngay!

Hiện thực hóa như thế nào?

Để có thể hiện thực hóa hoàn chỉnh micro frontend sẽ bao gồm rất nhiều thứ, ở đây chỉ tóm tắt một số vấn đề cơ bản cần giải quyết

Tương tác giữa các ứng dụng

Một câu hỏi được đặt ra đầu tiên là nếu tách ra thành nhiều bộ source như vậy, làm sao chúng có thể nói chuyện được với nhau? Một cách tổng quát, nên hạn chế việc trao đổi thông tin qua lại ít chừng nào tốt chừng đó, bởi vì nếu bạn làm ngược lại, nghĩa là bạn đang lặp lại vấn đề chúng ta muốn giải quyết ngay từ đâu: decoupling các tính năng với nhau.

Nhưng việc trao đổi giữa các ứng dụng với nhau là không tránh khỏi và cần thiết, chúng ta chỉ tiết chế chứ không loại bỏ hết, Custom event là một cách, cách khác, lấy mô hình truyền callback và data từ trên xuống trong React để làm kênh trao đổi thông tin, làm như thế nó sẽ rất tường minh, cách thứ 3 là thông qua thanh đường dẫn trên trình duyệt, chút nữa nói kỹ hơn.

Tựa chung, chúng ta không share state, mà chỉ share dữ liệu trong database như microservice.

Thư viện component dùng chung

Nó chung, ý tưởng re-use lại những component UI không có gì mới, nghe cũng rất hợp lý, mặc dù ai cũng biết việc đó khó làm.

Sai lầm thường thấy là việc tạo các component như vậy quá sớm, việc hào hứng quá mức vào xây dựng một Framework UI chuẩn không cần chỉnh, viết một lần xài mãi mãi, thống nhất giao diện trên mọi mặt trận là điều thường thấy ở mọi team. Tuy nhiên, trong thực tế, kinh nghiệm cho biết rằng việc đó rất khó, nếu không muốn nói là không thể, không thể ngồi nghĩ ra một bộ Framework với tất cả các API cần thiết rồi đưa cho tất cả các team xài, chắc gì API đó đã đáp ứng đúng nhu cầu cho tất cả các team? Lời khuyên là các team cứ tạo ra những component riêng trong codebase nếu họ thấy cần, dù cho nó có bị duplicate đây nữa cũng chẳng sao. Và khi đã chín mùi, những API nào cần thiết sẽ hiện nguyên hình, chúng ta đưa những cho đang bị duplicate vào trong thư viện dùng chung.

Tất nhiên cũng có những ngoại lệ, những component mà nhìn vào chúng ta biết ngay là cần đưa vào share component, như icon, label, button, autocomplete, drop-down, search, table. Và nhớ là chỉ đưa đúng UI logic, đừng đưa bất kỳ business logic và domain logic vào đây. Ví dụ như một component ProductTable cho riêng cái domain Product là không nên, chỉ nên làm một cái component Table.

Thoạt nghe làm một share component có vẻ đơn giản, nhưng nó lại là công việc đòi hỏi kỹ thuật phải rất cứng tay, và người có nhúng tay vào tất cả các team.

Styling

Styling 2020 là một câu chuyện dài, như mình đã kể trong một bài viết, tựa chung mà nói bạn có thể dùng BEM, dùng SASS, dùng CSS module, dùng CSS-in-JS, dùng Styled Component, dùng Tailwind, kiểu gì cũng được, miễn đảm bảo được style không chồng chéo lên nhau, thằng nào độc lập thằng đó, và tự tin đoạn code nó sẽ chạy như đúng như lường trước.

Các cách để integrate

Để hiện thực hóa ý tưởng của micro frontend, cũng có nhiều cách làm, cách nào cũng có đánh đổi. Tựu chung, nếu xét theo hướng giao diện, chúng ta có thể tổ chức nó theo dạng một ứng dụng dạng container, bao gồm những thành phần chung như headermenu, và các micro frontend sẽ nhúng vào phần ruột của trang

Micro frontend tại sao và như thế nào?Micro frontend tại sao và như thế nào?

Cách 1: composition dùng server side template

Với một cách không chính thống lắm cho việc phát triển code FE, chúng ta render HTML ở phía server, với nhiều bộ template khác nhau. Chúng ta có một file index.html với các phần tử chung, server sẽ quyết định phần ruột trả về cho từng trang

<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Feed me</title>
  </head>
  <body>
    <h1> Feed me</h1>
    <!--# include file="$PAGE.html" -->
  </body>
</html>

Ở ví dụ này đang dùng với Nginx, biến $PAGE sẽ ứng với URL đang được request

server {
    listen 8080;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;
    ssi on;

    # Redirect / đến /browse
    rewrite ^/$ http://localhost:8080/browse redirect;

    # Dùng HTML nào để insert dựa vào URL
    location /browse {
      set $PAGE 'browse';
    }
    location /order {
      set $PAGE 'order';
    }
    location /profile {
      set $PAGE 'profile'
    }

    # Cho phép render ở index.html
    error_page 404 /index.html;
}

Kỹ thuật này mình không nắm lắm, nên cũng chỉ để đây cho các bạn tham khảo, trong thực tế mình gặp và làm việc với những cách làm bên dưới nhiều hơn.

Integrate lúc build

Cách này sẽ publish cái micro frontend ở dạng package, container sẽ khai báo những micro frontend này ở dạng dependency. File package.json nó sẽ trông như thế này:

{
  "name": "@feed-me/container",
  "version": "1.0.0",
  "description": "A food delivery web app",
  "dependencies": {
    "@feed-me/browse-restaurants": "^1.2.3",
    "@feed-me/order-food": "^4.5.6",
    "@feed-me/user-profile": "^7.8.9"
  }
}

Thoạt nhìn, cũng khá hợp lý, tuy nhiên nếu để ý, bạn sẽ thấy chúng ta phải re-compile và release trên từng cục dependency, rồi sao đó lại phải release tiếp container. Đây vẫn không phải là cách làm được khuyến khích.

Integrate lúc run-time bằng iframe

Đây cũng là cách mà dự án mình đang dùng, một cách tiếp cận đơn giản nhất để compose nhiều ứng dụng với nhau trong trình duyệt đã có từ rất rất lâu. Lợi ích có thể kể thêm của cách làm này là phần styling và biến global đều độc lập và không bị đụng độ lẫn nhau

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <iframe id="micro-frontend-container"></iframe>

    <script type="text/javascript">
      const microFrontendsByRoute = {
        '/': 'https://browse.example.com/index.html',
        '/order-food': 'https://order.example.com/index.html',
        '/user-profile': 'https://profile.example.com/index.html',
      };

      const iframe = document.getElementById('micro-frontend-container');
      iframe.src = microFrontendsByRoute[window.location.pathname];
    </script>
  </body>
</html>

Nhược điểm của cách này là việc tích hợp giữa các phần của ứng dụng, như route, history, deep-link sẽ rất phức tạp, responsive cũng sẽ gặp nhiều vấn đề cần xử lý hơn.

Integrate lúc run-time bằng JavaScript

Đây là cách linh hoạt nhất, và được nhiều team chọn làm. Mỗi một micro frontend sẽ được nhét vào trong trang bằng thẻ <script />. Container sẽ làm nhiệm vụ cho mount micro frontend nào và thực thi các hàm liên quan để báo cho các micro frontend sẽ render ở đâu và khi nào.

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <!-- Nó không render bất cứ gì cả -->
    <!-- Nó sẽ đưa vào hàm entry-point vào `window` -->
    <script src="https://browse.example.com/bundle.js"></script>
    <script src="https://order.example.com/bundle.js"></script>
    <script src="https://profile.example.com/bundle.js"></script>

    <div id="micro-frontend-root"></div>

    <script type="text/javascript">
      // Những global function này được nhét vào window bằng các đoạn script include ở trên
      const microFrontendsByRoute = {
        '/': window.renderBrowseRestaurants,
        '/order-food': window.renderOrderFood,
        '/user-profile': window.renderUserProfile,
      };
      const renderFunction = microFrontendsByRoute[window.location.pathname];

      // Sau khi đã có các hàm cần thiết,
      // đưa id của element sẽ dùng để render
      renderFunction('micro-frontend-root');
    </script>
  </body>
</html>

Trên đây chỉ là ví dụ cơ bản nhất để mô tả kỹ thuật sẽ làm, thật tế có thể phải thêm thắt một số thứ khác. Không giống với cách integrate lúc build, bundle.js có thể được deploy một cách độc lập. Và khác iframe, chúng ta có thể linh động chọn lựa việc render micro frontend nào chúng ta thích.

Nếu có hứng thú với cách làm này, có thể tham khảo thêm ví dụ chi tiết hơn

Integrate lúc run-time bằng Web Component

Một lựa chọn khác cũng tương tự như cách làm trên, mỗi một micro frontend sẽ được link với element

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <!-- Chưa render gì cả -->
    <script src="https://browse.example.com/bundle.js"></script>
    <script src="https://order.example.com/bundle.js"></script>
    <script src="https://profile.example.com/bundle.js"></script>

    <div id="micro-frontend-root"></div>

    <script type="text/javascript">
      // Những element type này được định nghĩa ở các script trên
      const webComponentsByRoute = {
        '/': 'micro-frontend-browse-restaurants',
        '/order-food': 'micro-frontend-order-food',
        '/user-profile': 'micro-frontend-user-profile',
      };
      const webComponentType = webComponentsByRoute[window.location.pathname];

      // Tạo instance và đưa vào document ứng với từng loại phù hợp
      const root = document.getElementById('micro-frontend-root');
      const webComponent = document.createElement(webComponentType);
      root.appendChild(webComponent);
    </script>
  </body>
</html>

Khác nhau duy nhất so với cách trên có lẽ chỉ là việc dùng web component thay vì một interface chúng ta tự định nghĩa.

Trao đổi giữa Backend

Cái này chưa biết, không dám chém.

Kết

Micro frontend có thể không lạ với một số người và khá mới với số còn lại, thực tế mà nói đã có rất nhiều dự án đang áp dụng kiến trúc này (dự án mình đang làm).

Cùng hy vọng với bài viết này bạn đã thấy công việc của những lập trình viên frontend không còn đơn thuần là việc làm sao cho trang web bay, lượn, responsive mượt mà, nếu bạn muốn tiến xa hơn, giới hạn là chân trời.

Các bài viết đã tham khảo

Bài viết gốc được đăng tải tại vuilaptrinh.com

Có thể bạn quan tâm:

Xem thêm các việc làm IT hấp dẫn tại Station D

Bài viết liên quan

Ngành IT: Làm việc “trên mây” kiếm nhiều tiền nhất hiện nay

Ngành IT: Làm việc “trên mây” kiếm nhiều tiền nhất hiện nay

Kết quả từ cuộc khảo sát đầu năm của Station D về lương bổng của lập trình viên cho thấy nhiều thay đổi đã và đang diễn ra trong ngành IT – cuộc khảo sát tập trung vào các câu hỏi về khối lượng công việc, triển vọng cũng như...

By stationd
Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!

Đâu chỉ mỗi Bitcoin, công nghệ Blockchain còn nhiều ứng dụng hơn thế!

Khi nhắc đến blockchain , lập tức mọi người thường nghĩ ngay đến các loại tiền mã hóa, chẳng hạn như bitcoin. Tuy nhiên, blockchain lại là công nghệ tạo ra tiền mã hóa nhưng bản thân công nghệ này không phải là tiền mã hóa như cách mà chúng...

By stationd
Mock phương thức static trong Unit Test sử dụng PowerMock

Mock phương thức static trong Unit Test sử dụng PowerMock

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh Trong bài viết này, mình sẽ hướng dẫn các bạn Mock các phương thức static trong Unit Test các bạn nhé! Nếu bạn nào chưa biết về Mock trong Unit Test thì mình có thể nói sơ qua...

By stationd
Một "thuật ngữ ma" đã tồn tại 75 năm trên internet, nó đang "ám" vào các mô hình AI, và sẽ còn tiếp tục tồn tại cho đến vĩnh cửu

Một "thuật ngữ ma" đã tồn tại 75 năm trên internet, nó đang "ám" vào các mô hình AI, và sẽ còn tiếp tục tồn tại cho đến vĩnh cửu

Một lời cảnh báo cho những người thích trích dẫn kiểu "nguồn sưu tầm", "nguồn internet" hay "nguồn AI", họ có thể sẽ đào lên được những "hóa thạch số" vô nghĩa.

By admin
Cảnh Báo Malware Giả Mạo Hợp Đồng Việc Làm: Tập Tin .EXE Nguy Hiểm Đội Lốt PDF/Word

Cảnh Báo Malware Giả Mạo Hợp Đồng Việc Làm: Tập Tin .EXE Nguy Hiểm Đội Lốt PDF/Word

Kẻ xấu đang lợi dụng nhu cầu tìm việc để phát tán phần mềm độc hại (malware) dưới dạng tệp 'hợp đồng' giả mạo. Hãy cảnh giác với những file có icon Word/PDF nhưng thực chất là .exe. Nếu mở, máy tính của bạn có thể bị đánh cắp toàn bộ thông tin cá nhân, cookie và mật khẩu.

By admin