Frontend·Hacker News·0 lượt xem

Ba trụ cột của sự phình to của JavaScript

The Three Pillars of JavaScript Bloat

AI Summary

Việc các dự án JavaScript bị phình to do quá nhiều dependency chủ yếu xuất phát từ ba nguyên nhân chính: 1. **Hỗ trợ các môi trường chạy (runtimes) cũ:** Nhiều thư viện vẫn cần hỗ trợ các phiên bản JavaScript rất cũ, thiếu các tính năng native ES5 trở lên. Điều này buộc chúng phải bao gồm các polyfill hoặc đoạn mã tương thích, làm tăng kích thước. 2. **Bảo vệ khỏi sự thay đổi global namespace ("primordials"):** Để tránh các đoạn mã độc hại hoặc thư viện khác làm hỏng các biến toàn cục quan trọng (như `Array`, `Object`), các dependency thường cần các cơ chế "primordials" để đảm bảo chúng hoạt động với phiên bản gốc của các constructor này. 3. **Xử lý giá trị cross-realm:** Khi làm việc với các môi trường độc lập nhau (ví dụ, trong `iframe` hoặc web worker), các constructor của cùng một kiểu dữ liệu có thể khác nhau giữa các realm. Các dependency cần có cách để xử lý sự khác biệt này. Các developer nên lưu ý rằng có rất nhiều gói utility nhỏ lẻ được tạo ra để giải quyết những vấn đề này, thường là cho các tình huống khá đặc thù. Trước khi đưa một dependency vào dự án, hãy cân nhắc kỹ: liệu việc nâng cấp môi trường chạy lên phiên bản mới hơn có thể loại bỏ được nhu cầu này không, hoặc liệu có thể áp dụng các chiến lược cô lập (isolation strategies) khác để giảm thiểu dấu chân dependency của dự án hay không.

Trong vài năm qua, chúng tôi đã chứng kiến ​​sự phát triển đáng kể của cộng đồng e18e và nhờ đó mà số lượng đóng góp tập trung vào hiệu suất cũng tăng lên. Phần lớn trong số này là sáng kiến ​​“dọn dẹp”, trong đó...

Trong vài năm qua, chúng tôi đã chứng kiến sự phát triển đáng kể của cộng đồng e18e và nhờ đó mà số lượng đóng góp tập trung vào hiệu suất cũng tăng lên. Phần lớn trong số này là sáng kiến “dọn dẹp”, trong đó cộng đồng đã loại bỏ các gói dư thừa, lỗi thời hoặc không được bảo trì.

Một trong những chủ đề phổ biến nhất xuất hiện trong chủ đề này là "sự phình to của phần phụ thuộc" - ý tưởng rằng các cây phụ thuộc npm ngày càng lớn hơn theo thời gian, thường có mã dự phòng từ lâu mà nền tảng hiện cung cấp nguyên bản.

Trong bài đăng này, tôi muốn xem xét ngắn gọn ba loại lỗi chính trong cây phụ thuộc của chúng ta, lý do chúng tồn tại và cách chúng ta có thể bắt đầu giải quyết chúng.

1. Hỗ trợ thời gian chạy cũ hơn (với sự an toàn và các lĩnh vực)

biểu đồ phụ thuộc chuỗi is

Biểu đồ trên là hình ảnh thường thấy trong nhiều cây phụ thuộc npm - một hàm tiện ích nhỏ dành cho một thứ có vẻ như vốn đã có sẵn, theo sau là nhiều phần phụ thuộc sâu nhỏ tương tự.

Vậy tại sao lại như vậy? Tại sao chúng ta cần is-string thay vì kiểm tra typeof? Tại sao chúng ta cần hasown thay vì Object.hasOwn (hoặc Object.prototype.hasOwnProperty)? Ba điều:

  1. Hỗ trợ cho các công cụ rất cũ
  2. Bảo vệ chống lại đột biến không gian tên toàn cầu
  3. Giá trị đa lĩnh vực

Hỗ trợ cho các động cơ rất cũ

Ở một nơi nào đó trên thế giới, dường như có một số người cần hỗ trợ ES3 - hãy nghĩ đến IE6/7 hoặc các phiên bản cực kỳ đầu tiên của Node.js.1

Đối với những người này, phần lớn những gì chúng ta coi là đương nhiên ngày nay đều không tồn tại. Ví dụ: họ không có bất kỳ điều nào sau đây:

  • Array.prototype.forEach
  • Array.prototype.reduce
  • Object.keys
  • Object.defineProperty

Đây đều là những tính năng của ES5, nghĩa là chúng không tồn tại trong các công cụ ES3.

Đối với những linh hồn bất hạnh vẫn đang chạy động cơ cũ này, họ cần phải tự mình thực hiện lại mọi thứ hoặc được cung cấp các polyfill.

Ngoài ra, điều thực sự tuyệt vời là nếu họ nâng cấp.

Bảo vệ chống lại đột biến không gian tên toàn cầu

Lý do thứ hai cho một số gói này là “sự an toàn”.

Về cơ bản, bên trong Node có một khái niệm về “nguyên thủy”. Về cơ bản, đây chỉ là các đối tượng toàn cầu được bao bọc khi khởi động và được Node nhập từ đó trở đi, để tránh việc Node bị phá vỡ do ai đó làm thay đổi không gian tên chung.

Ví dụ: nếu bản thân Node sử dụng Map và chúng tôi xác định lại Map là gì - chúng tôi có thể phá vỡ Node. Để tránh điều này, Node giữ một tham chiếu đến Bản đồ ban đầu mà nó nhập thay vì truy cập vào toàn cục.

Bạn có thể đọc thêm về điều này tại đây trong kho lưu trữ Node.

Điều này rất có ý nghĩa đối với một động cơ , vì nó thực sự sẽ không bị đổ nếu một tập lệnh làm xáo trộn không gian tên chung.

Một số nhà bảo trì cũng tin rằng đây cũng là cách chính xác để xây dựng gói. Đây là lý do tại sao chúng tôi có các phần phụ thuộc như math-intrinsics trong biểu đồ trên, về cơ bản sẽ tái xuất các hàm Math.* khác nhau để tránh đột biến.

Cross-realm values

Cuối cùng, chúng ta có các giá trị liên vùng. Về cơ bản, đây là những giá trị bạn đã chuyển từ lĩnh vực này sang lĩnh vực khác - ví dụ: từ trang web sang