Công cụ xây dựng giao diện người dùng đóng vai trò quan trọng đối với quy trình làm việc của nhà phát triển giao diện người dùng hiện đại vì nhiều lý do được phân loại theo trải nghiệm của người dùng và nhà phát triển được cải thiện. Theo quan điểm của nhà phát triển, công cụ giao diện người dùng cung cấp cho chúng ta: khả năng tạo mô-đun, máy chủ phát triển để phát triển cục bộ, Thay thế mô-đun nóng (HMR) để có vòng phản hồi ngắn hơn trong chế độ phát triển, khả năng nhắm mục tiêu đến các trình duyệt cũ bằng polyfill, xử lý nhiều loại tệp ngoài JavaScript, danh sách còn dài.
Do đó, người dùng có thể tận hưởng các ứng dụng có nhiều khả năng và tính năng hơn, vẫn hoạt động hiệu quả thông qua các kỹ thuật như phân tách mã, lưu vào bộ nhớ đệm, tìm nạp trước và các kỹ thuật tối ưu hóa tài nguyên khác — với một số ứng dụng thậm chí có thể hoạt động ngoại tuyến.
Công cụ giao diện người dùng ngày nay cung cấp cho chúng ta rất nhiều thứ đến nỗi khó có thể tưởng tượng rằng đã có thời điểm chúng thậm chí không cần thiết. Một chuyến đi ngược dòng ký ức có thể giúp chúng ta hiểu được cách chúng ta đến đây.
Không có ứng dụng "nặng về JavaScript" phức tạp nào, vì vậy, không cần bất kỳ công cụ nào để biên soạn và vận chuyển JavaScript tốt hơn, nhưng điều đó sẽ không còn đúng nữa.
Theo thời gian, chúng tôi bắt đầu tạo ra nhiều trải nghiệm người dùng phức tạp hơn trên web, chúng tôi đã chuyển từ các trang web tĩnh sang các ứng dụng web có tính động cao phục vụ dữ liệu cụ thể của người dùng. Các ứng dụng này yêu cầu nhiều JavaScript hơn so với các ứng dụng truyền thống và giới hạn khi làm việc với JavaScript trở nên rõ ràng hơn rất nhiều.
Có hai cách chính để tải JavaScript trong trình duyệt. Một là với thẻ script tham chiếu đến tệp JavaScript và cách còn lại là viết JavaScript trực tiếp vào HTML bên trong thẻ script.
Giới hạn này khi tải JavaScript trở thành nút thắt cổ chai khi bạn phải tải nhiều JavaScript. Có những giới hạn của trình duyệt khi tải nhiều tệp JavaScript đồng thời và các vấn đề về khả năng bảo trì khi có một tệp JavaScript lớn (như kích thước tệp, vấn đề về phạm vi, xung đột không gian tên, v.v.).
Chúng tôi đã đưa ra các giải pháp như Biểu thức hàm được gọi ngay lập tức (IIFE) để hỗ trợ đóng gói và một số vấn đề về phạm vi sau đó, chúng tôi có khả năng viết JavaScript của mình trong nhiều tệp khác nhau. Sau đó, chúng tôi cần một cách để kết hợp nhiều tệp này thànhmột tệp để phục vụ trong trình duyệt
Khi các ứng dụng của chúng tôi trở nên phức tạp hơn, các vấn đề như thiếu loại bỏ mã chết, xây dựng lại hoàn toàn cho các thay đổi nhỏ và các vấn đề về hiệu suất khác khiến chúng tôi nhận ra rằng chúng tôi cần thứ gì đó nhiều hơn là chỉ nối. Điều đó đã tạo ra các bundler hiện đại hơn như Webpack, Parcel và các bundler khác.
Với tốc độ tiến bộ trong không gian frontend không chậm lại, chúng tôi đã bắt đầu nhận thấy các khoảng cách và vấn đề với các công cụ xây dựng hiện đại.
Một số hạn chế chính bao gồm:
Kiểm tra kỹ hơn sẽ thấy rằng các công cụ mới này dường như đang áp dụng hai cách tiếp cận chính để giải quyết vấn đề (không nhất thiết phải loại trừ lẫn nhau): thay đổi mô hình và thay đổi nền tảng — cả hai đều được thúc đẩy bởi những tiến bộ mới trong hệ sinh thái phát triển web.
Là một ngôn ngữ cấp cao, JavaScript không thể đạt đến mức hiệu suất gốc. Điều này có nghĩa là các công cụ được xây dựng trên nền tảng này có giới hạn về hiệu suất của chúng. Vì vậy, để thoát khỏi hạn chế này, các công cụ xây dựng mới hơn đang được xây dựng trên các ngôn ngữ cấp thấp hơn, vốn có hiệu suất cao hơn như Rust.
Các ngôn ngữ như Rust và Go đã trở thành các lựa chọn phổ biến để biên soạn thế hệ công cụ xây dựng tiếp theo với trọng tâm là hiệu suất. Rust, nói riêng, không chỉ phổ biến vì hiệu suất mà còn vì trải nghiệm dành cho nhà phát triển ấn tượng — được bình chọn là ngôn ngữ lập trình “được yêu thích nhất” trong sáu năm liên tiếp trong Khảo sát nhà phát triển Stack Overflow.
Khi nói về quyết định xây dựng Rome (công cụ xây dựng chứ không phải thành phố) bằng Rust, Jamie Kyle cho biết:
Các dự án như SWC chứng minh rằng với sự thay đổi của nền tảng cơ bản, hiệu suất của các công cụ xây dựng có thể được cải thiện đáng kể.
Để cải thiện hiệu suất ở chế độ phát triển, rất nhiều tối ưu hóa mất nhiều thời gian hơn để hoàn thành đã bị loại bỏ và thay vào đó được chạy khi chúng ta đóng gói ứng dụng sản xuất của mình. Điều này đảm bảo rằng mất ít thời gian nhất có thể để khởi động máy chủ dev, chạy ứng dụng của chúng tôi ở chế độ phát triển và đạt được hiệu quả.
Tuy nhiên, quá trình đóng gói vẫn mất khá nhiều thời gian và khi dự án phát triển, thời gian xây dựng (kể cả trong quá trình phát triển) chỉ ngày càng dài hơn. Sẽ thật tuyệt nếu chúng ta có thể bỏ qua hoàn toàn việc đóng gói trong khi vẫn có thể viết các mô-đun như bình thường và trình duyệt hiểu cách làm việc với chúng? Một bộ công cụ xây dựng mới đang áp dụng cách tiếp cận này, được gọi là Phát triển không đóng gói.
Phát triển không đóng gói rất tuyệt. Nó giải quyết một vấn đề lớn với các công cụ xây dựng hiện có: chúng thường cần xây dựng lại toàn bộ các phần của ứng dụng của bạn ngay cả đối với những thay đổi mã nhỏ và thời gian xây dựng dài hơn khi ứng dụng phát triển. Chúng ta mất đi phản hồi nhanh chóng — điều cần thiết cho trải nghiệm phát triển dễ chịu.
Người ta có thể tự hỏi, nếu phát triển không đóng gói tuyệt vời như vậy, tại sao ngày nay nó lại không phải là chuẩn mực? Có hai lý do chính khiến phát triển không gói gọn chỉ mới bắt đầu thu hút sự chú ý: khả năng tương thích của trình duyệt đối với các tính năng tiên tiến và xử lý nhập mô-đun nút.
Các mô-đun ES đã tồn tại trong một thời gian khá dài. Tuy nhiên, chúng ta chỉ có thể bắt đầu tận dụng nó cho những thứ như phát triển không trọn gói, chủ yếu là do thời gian chuẩn hóa của nó trên tất cả các bên tham gia trong hệ sinh thái web.
Trong bài viết của mình về ES Modules trên Mozilla Hacks, Lin Clark cho biết:
Hãy thử truy cập một dự án StackBlitz bằng Safari và bạn sẽ thấy màn hình sau thông báo về việc thiếu hỗ trợ cho WebContainer trong các trình duyệt không dựa trên Chromium.

Tốc độ áp dụng tính năng không giống nhau giữa các nhà cung cấp trình duyệt và thường có sự khác biệt về cách các nhà cung cấp khác nhau triển khai một số tính năng nhất định. Tuy nhiên, tương lai có vẻ tươi sáng với các sáng kiến như Interop 2022.
Cố gắng tải trực tiếp phần này vào trình duyệt, như chúng ta cần làm đối với quá trình phát triển không đóng gói, sẽ dẫn đến hai vấn đề. Đầu tiên, trình duyệt không biết cách giải quyết đường dẫn để tìm
Vấn đề sau là vấn đề lớn hơn ở đây, vì có thể, và thậm chí là đơn giản, chỉ cần thay thế các lệnh nhập mô-đun node của chúng ta bằng các đường dẫn tương đối đến các tệp cụ thể. Tuy nhiên, thực tế là hầu hết các gói NPM được viết theo định dạng mô-đun phù hợp hơn với Node JS so với trình duyệt đòi hỏi các phụ thuộc NPM của chúng tôi phải được xử lý đặc biệt để tạo điều kiện cho quá trình phát triển không đóng gói.
Snowpack, nói riêng, xử lý điều này bằng cách xử lý các phụ thuộc của ứng dụng thành các tệp Javascript riêng biệt sau đó có thể được sử dụng trực tiếp trong trình duyệt. Bạn có thể tìm hiểu thêm về cách Snowpack thực hiện điều này tại đây.
Với ES Modules hiện đang là xu hướng chính trong hầu hết các trình duyệt hiện đại và các giải pháp thay thế thông minh cho các phụ thuộc NPM, các công cụ xây dựng như Vite và Snowpack có thể cung cấp tùy chọn phát triển không gói với hiệu suất được cải thiện đáng kể, bản dựng nhanh, bên cạnh HMR siêu nhanh.
Với trọng tâm lớn vào hiệu suất, tính dễ sử dụng và cấu hình ít phức tạp hơn, thế hệ công cụ xây dựng tiếp theo đã sẵn sàng cung cấp năng lượng cho các ứng dụng frontend đầy tham vọng trong tương lai.
Bạn có hào hứng với những phát triển gần đây trong lĩnh vực này không? Hãy cho tôi biết trong phần bình luận suy nghĩ của bạn về những cải tiến sắp tới và bối cảnh hiện tại.
Do đó, người dùng có thể tận hưởng các ứng dụng có nhiều khả năng và tính năng hơn, vẫn hoạt động hiệu quả thông qua các kỹ thuật như phân tách mã, lưu vào bộ nhớ đệm, tìm nạp trước và các kỹ thuật tối ưu hóa tài nguyên khác — với một số ứng dụng thậm chí có thể hoạt động ngoại tuyến.
Công cụ giao diện người dùng ngày nay cung cấp cho chúng ta rất nhiều thứ đến nỗi khó có thể tưởng tượng rằng đã có thời điểm chúng thậm chí không cần thiết. Một chuyến đi ngược dòng ký ức có thể giúp chúng ta hiểu được cách chúng ta đến đây.
Quá khứ
Trước khi các ứng dụng frontend trở nên phức tạp như ngày nay, JavaScript chỉ được sử dụng để thêm tính tương tác cơ bản vào các tài liệu HTML đơn giản — tương tự như cách Adobe Flash được sử dụng.Không có ứng dụng "nặng về JavaScript" phức tạp nào, vì vậy, không cần bất kỳ công cụ nào để biên soạn và vận chuyển JavaScript tốt hơn, nhưng điều đó sẽ không còn đúng nữa.
Theo thời gian, chúng tôi bắt đầu tạo ra nhiều trải nghiệm người dùng phức tạp hơn trên web, chúng tôi đã chuyển từ các trang web tĩnh sang các ứng dụng web có tính động cao phục vụ dữ liệu cụ thể của người dùng. Các ứng dụng này yêu cầu nhiều JavaScript hơn so với các ứng dụng truyền thống và giới hạn khi làm việc với JavaScript trở nên rõ ràng hơn rất nhiều.
Có hai cách chính để tải JavaScript trong trình duyệt. Một là với thẻ script tham chiếu đến tệp JavaScript và cách còn lại là viết JavaScript trực tiếp vào HTML bên trong thẻ script.
Mã:
var a = 1; var b = 2; var result = a + b;
Chúng tôi đã đưa ra các giải pháp như Biểu thức hàm được gọi ngay lập tức (IIFE) để hỗ trợ đóng gói và một số vấn đề về phạm vi sau đó, chúng tôi có khả năng viết JavaScript của mình trong nhiều tệp khác nhau. Sau đó, chúng tôi cần một cách để kết hợp nhiều tệp này thànhmột tệp để phục vụ trong trình duyệt
The Present
Có thể chia JavaScript của chúng tôi thành các tệp khác nhau bằng IIFE, có vẻ như tất cả những gì chúng tôi cần là một cách để nối các tệp này và chuyển một tệp duy nhất đến trình duyệt. Nhu cầu này đã chứng kiến sự ra đời của các công cụ như Gulp, Grunt, Brocolli, v.v. Tuy nhiên, chúng tôi sớm nhận ra rằng suy nghĩ của mình có thể hơi đơn giản.Khi các ứng dụng của chúng tôi trở nên phức tạp hơn, các vấn đề như thiếu loại bỏ mã chết, xây dựng lại hoàn toàn cho các thay đổi nhỏ và các vấn đề về hiệu suất khác khiến chúng tôi nhận ra rằng chúng tôi cần thứ gì đó nhiều hơn là chỉ nối. Điều đó đã tạo ra các bundler hiện đại hơn như Webpack, Parcel và các bundler khác.
Với tốc độ tiến bộ trong không gian frontend không chậm lại, chúng tôi đã bắt đầu nhận thấy các khoảng cách và vấn đề với các công cụ xây dựng hiện đại.
Một số hạn chế chính bao gồm:
- Thiết lập và cấu hình phức tạp của một số bundler hiện có này;
- Tăng thời gian xây dựng khi các ứng dụng lớn hơn;
- Hiệu suất không tối ưu ở chế độ phát triển.
Tương lai
Những hạn chế của các công cụ xây dựng chính thống hiện nay đã dẫn đến một số nỗ lực nhằm tái hiện lại những gì một công cụ xây dựng front-end nên có và làm được, và hiện nay có khá nhiều công cụ xây dựng mới đang được sử dụng.Kiểm tra kỹ hơn sẽ thấy rằng các công cụ mới này dường như đang áp dụng hai cách tiếp cận chính để giải quyết vấn đề (không nhất thiết phải loại trừ lẫn nhau): thay đổi mô hình và thay đổi nền tảng — cả hai đều được thúc đẩy bởi những tiến bộ mới trong hệ sinh thái phát triển web.
Một nền tảng mới
Các công cụ xây dựng frontend theo truyền thống được xây dựng bằng JavaScript và gần đây hơn là Typescript. Điều này có lý vì JavaScript là ngôn ngữ của web và việc biên soạn các công cụ xây dựng cho web bằng cùng một ngôn ngữ giúp nhiều người dễ dàng đóng góp vào nỗ lực này và xây dựng cộng đồng xung quanh các công cụ này. Tuy nhiên, vẫn có những vấn đề cố hữu với cách tiếp cận này.Là một ngôn ngữ cấp cao, JavaScript không thể đạt đến mức hiệu suất gốc. Điều này có nghĩa là các công cụ được xây dựng trên nền tảng này có giới hạn về hiệu suất của chúng. Vì vậy, để thoát khỏi hạn chế này, các công cụ xây dựng mới hơn đang được xây dựng trên các ngôn ngữ cấp thấp hơn, vốn có hiệu suất cao hơn như Rust.
Các ngôn ngữ như Rust và Go đã trở thành các lựa chọn phổ biến để biên soạn thế hệ công cụ xây dựng tiếp theo với trọng tâm là hiệu suất. Rust, nói riêng, không chỉ phổ biến vì hiệu suất mà còn vì trải nghiệm dành cho nhà phát triển ấn tượng — được bình chọn là ngôn ngữ lập trình “được yêu thích nhất” trong sáu năm liên tiếp trong Khảo sát nhà phát triển Stack Overflow.
Khi nói về quyết định xây dựng Rome (công cụ xây dựng chứ không phải thành phố) bằng Rust, Jamie Kyle cho biết:
Dự án SWC đi đầu trong ý tưởng sử dụng Rust làm công cụ xây dựng giao diện người dùng. Hiện tại, nó đang cung cấp năng lượng cho các dự án như trình biên dịch mới của Next.js, Deno, Parcel và các dự án khác — với hiệu suất cao hơn nhiều lần so với các công cụ xây dựng hiện có khác.“Nhiều người khác đã truyền đạt lợi ích về hiệu suất, bộ nhớ và tính an toàn của Rust trước chúng tôi — hãy nói rằng bất kỳ ai từng nói Rust tốt đều đúng. Tuy nhiên, mối quan tâm lớn nhất của chúng tôi là năng suất của chính chúng tôi.[...]Tuy nhiên, sau một số lần tạo mẫu, chúng tôi nhanh chóng nhận ra rằng chúng tôi thực sự có thể làm việc hiệu quả hơn trong Rust”
— Jamie Kyle trong Rome Will Be Written In Rust
Các dự án như SWC chứng minh rằng với sự thay đổi của nền tảng cơ bản, hiệu suất của các công cụ xây dựng có thể được cải thiện đáng kể.
Sự thay đổi mô hình
Cách thức hoạt động của một đường ống xây dựng giao diện người dùng điển hình hiện nay là bạn tạo các mô-đun JavaScript trong nhiều tệp khác nhau, chạy lệnh, công cụ xây dựng sẽ chọn các mô-đun này, đóng gói chúng thành một mô-đun duy nhất, chuyển đổi chúng thành định dạng mà trình duyệt có thể hiểu và phục vụ tệp đó trong trình duyệt.Để cải thiện hiệu suất ở chế độ phát triển, rất nhiều tối ưu hóa mất nhiều thời gian hơn để hoàn thành đã bị loại bỏ và thay vào đó được chạy khi chúng ta đóng gói ứng dụng sản xuất của mình. Điều này đảm bảo rằng mất ít thời gian nhất có thể để khởi động máy chủ dev, chạy ứng dụng của chúng tôi ở chế độ phát triển và đạt được hiệu quả.
Tuy nhiên, quá trình đóng gói vẫn mất khá nhiều thời gian và khi dự án phát triển, thời gian xây dựng (kể cả trong quá trình phát triển) chỉ ngày càng dài hơn. Sẽ thật tuyệt nếu chúng ta có thể bỏ qua hoàn toàn việc đóng gói trong khi vẫn có thể viết các mô-đun như bình thường và trình duyệt hiểu cách làm việc với chúng? Một bộ công cụ xây dựng mới đang áp dụng cách tiếp cận này, được gọi là Phát triển không đóng gói.
Phát triển không đóng gói rất tuyệt. Nó giải quyết một vấn đề lớn với các công cụ xây dựng hiện có: chúng thường cần xây dựng lại toàn bộ các phần của ứng dụng của bạn ngay cả đối với những thay đổi mã nhỏ và thời gian xây dựng dài hơn khi ứng dụng phát triển. Chúng ta mất đi phản hồi nhanh chóng — điều cần thiết cho trải nghiệm phát triển dễ chịu.
Người ta có thể tự hỏi, nếu phát triển không đóng gói tuyệt vời như vậy, tại sao ngày nay nó lại không phải là chuẩn mực? Có hai lý do chính khiến phát triển không gói gọn chỉ mới bắt đầu thu hút sự chú ý: khả năng tương thích của trình duyệt đối với các tính năng tiên tiến và xử lý nhập mô-đun nút.
1. Khả năng tương thích của trình duyệt đối với các tính năng tiên tiến
Phát triển không gói gọn được hỗ trợ bởi ES Modules (ESM), mang đến một hệ thống mô-đun chuẩn hóa cho JavaScript — được hỗ trợ gốc trên nhiều thời gian chạy, bao gồm cả trình duyệt. Với khả năng mới này, chúng ta có thể đánh dấu các thẻ tập lệnh của mình là mô-đun và do đó có thể sử dụng các từ khóaimport
và export
mà chúng ta đều quen thuộc;
Mã:
/** Mã mô-đun JavaScript nằm ở đây */
Trong bài viết của mình về ES Modules trên Mozilla Hacks, Lin Clark cho biết:
Vấn đề về hỗ trợ trình duyệt hoặc thiếu hỗ trợ đã gây khó khăn cho quá trình phát triển giao diện người dùng trong một thời gian dài. Đây là lý do tại sao chúng ta có tiền tố nhà cung cấp CSS của mình, đôi khi là lý do cho polyfill, tại sao chúng ta dành thời gian đảm bảo hỗ trợ đa nền tảng cho các ứng dụng web của mình và tại sao đôi khi phải mất khá nhiều thời gian trước khi chúng ta có thể tận dụng các tính năng web mới nhất và tuyệt vời nhất trong công việc hàng ngày của mình.“Các mô-đun ES mang đến một hệ thống mô-đun chính thức, được chuẩn hóa cho JavaScript. Tuy nhiên, phải mất một thời gian để đạt được điều này — gần 10 năm làm việc chuẩn hóa.”
— Lin Clark trong Các mô-đun ES: Một cuộc lặn sâu về phim hoạt hình
Hãy thử truy cập một dự án StackBlitz bằng Safari và bạn sẽ thấy màn hình sau thông báo về việc thiếu hỗ trợ cho WebContainer trong các trình duyệt không dựa trên Chromium.

Tốc độ áp dụng tính năng không giống nhau giữa các nhà cung cấp trình duyệt và thường có sự khác biệt về cách các nhà cung cấp khác nhau triển khai một số tính năng nhất định. Tuy nhiên, tương lai có vẻ tươi sáng với các sáng kiến như Interop 2022.
2. Xử lý nhập mô-đun Node
Hầu hết, nếu không muốn nói là tất cả, các ứng dụng frontend mà chúng ta viết ngày nay đều phụ thuộc vào các thư viện bên ngoài từ NPM. Đối với một ứng dụng react thông thường, chúng ta sẽ import react ở đầu các tệp thành phần như sau:
Mã:
import React from 'react'/** Phần còn lại của mã thành phần */
react
và thứ hai, thư viện react được xuất bản dưới dạng mô-đun Common JS (CJS) — không thể chạy gốc trong trình duyệt nếu không có một số xử lý trước.Vấn đề sau là vấn đề lớn hơn ở đây, vì có thể, và thậm chí là đơn giản, chỉ cần thay thế các lệnh nhập mô-đun node của chúng ta bằng các đường dẫn tương đối đến các tệp cụ thể. Tuy nhiên, thực tế là hầu hết các gói NPM được viết theo định dạng mô-đun phù hợp hơn với Node JS so với trình duyệt đòi hỏi các phụ thuộc NPM của chúng tôi phải được xử lý đặc biệt để tạo điều kiện cho quá trình phát triển không đóng gói.
Snowpack, nói riêng, xử lý điều này bằng cách xử lý các phụ thuộc của ứng dụng thành các tệp Javascript riêng biệt sau đó có thể được sử dụng trực tiếp trong trình duyệt. Bạn có thể tìm hiểu thêm về cách Snowpack thực hiện điều này tại đây.
Với ES Modules hiện đang là xu hướng chính trong hầu hết các trình duyệt hiện đại và các giải pháp thay thế thông minh cho các phụ thuộc NPM, các công cụ xây dựng như Vite và Snowpack có thể cung cấp tùy chọn phát triển không gói với hiệu suất được cải thiện đáng kể, bản dựng nhanh, bên cạnh HMR siêu nhanh.
Suy nghĩ cuối cùng
Phát triển frontend đã có nhiều tiến bộ và nhu cầu của chúng ta không ngừng phát triển và ngày càng phức tạp hơn. Công cụ xây dựng là một phần thiết yếu trong cách chúng ta xây dựng các ứng dụng frontend và các công cụ hiện tại đang không đạt được mục tiêu, thúc đẩy sự phát triển của các công cụ mới giúp định hình lại cách thiết kế các công cụ xây dựng.Với trọng tâm lớn vào hiệu suất, tính dễ sử dụng và cấu hình ít phức tạp hơn, thế hệ công cụ xây dựng tiếp theo đã sẵn sàng cung cấp năng lượng cho các ứng dụng frontend đầy tham vọng trong tương lai.
Bạn có hào hứng với những phát triển gần đây trong lĩnh vực này không? Hãy cho tôi biết trong phần bình luận suy nghĩ của bạn về những cải tiến sắp tới và bối cảnh hiện tại.
Đọc thêm
- “Cách duy trì ứng dụng Next.js lớn”, Nirmalya Ghosh
- “Phân tích các bản dựng cồng kềnh bằng Netlify và Next.js”, Átila Fassina
- “Bắt đầu với Webpack”, Nwani Victory
- “Công cụ (Dev) đó là gì?”, Patrick Brosset