Tối Ưu Hóa Angular Signals với Kiểm Tra Đối Tượng Thông Minh
Giới Thiệu
Trong Angular, Signals là một công cụ mạnh mẽ để xử lý tính năng phản ứng (reactivity). Tuy nhiên, nếu không được quản lý cẩn thận, chúng có thể gây ra những cập nhật không cần thiết, yêu cầu lãng phí và các vấn đề về hiệu suất. Bài viết này sẽ khám phá cách Signals phát ra các cập nhật, tầm quan trọng của việc kiểm tra sự bằng nhau (equal
) và cách triển khai các chiến lược so sánh sâu hiệu quả để giữ cho ứng dụng của bạn mượt mà và đáng tin cậy.
Cách Signals Phát Ra Cập Nhật
Mỗi khi tham chiếu thay đổi, Signal được coi là “bẩn” và tất cả các Signal phụ, DOM, và các hàm effect
cũng sẽ cập nhật. Điều này có thể dẫn đến việc cập nhật không cần thiết nếu không có biện pháp kiểm tra hợp lý.
Ngăn Ngừa Cập Nhật Không Cần Thiết với equal
Để tránh các cập nhật không cần thiết trong pipeline của Signal, bạn có thể sử dụng tùy chọn equal
để định nghĩa logic so sánh một cách thủ công. Điều này có vẻ đơn giản với các đối tượng nông, nhưng với các đối tượng sâu, công việc trở nên nặng nề và khó duy trì hơn.
Tài Nguyên Angular và Cập Nhật Phiền Phức từ Nguồn
Vấn đề tương tự cũng tồn tại với Angular Resource
. Bạn có thể sử dụng Signal như một nguồn cho tùy chọn params
. Điều này kích hoạt một cuộc gọi bất đồng bộ mỗi khi Signal phát ra một giá trị mới. Tuy nhiên, mỗi cuộc gọi mới hủy bỏ cuộc gọi trước đó (tương tự như switchMap
trong RxJS). Điều này không chỉ gây ra các yêu cầu không cần thiết mà còn loại bỏ giá trị trước đó khỏi Resource
, có thể dẫn đến các hiệu ứng phụ kỳ lạ như việc hiển thị trang không ổn định.
Hiệu Suất của equal
với Các Đối Tượng Lồng Ghép Sâu
Để ngăn ngừa các cập nhật không cần thiết, bạn có thể thêm các kiểm tra sự bằng nhau thủ công trước khi gọi mySignal.set(...)
hoặc mySignal.update(...)
. Nhưng điều này dễ bị quên. Đó là lý do tại sao tôi thích sử dụng tùy chọn equal
:
javascript
signal({ id: 1 }, { equal: (a, b) => a.id === b.id });
Tuy nhiên, khi xử lý các đối tượng lồng ghép sâu, các kiểm tra sự bằng nhau có thể trở nên tốn kém. Andrew Jarrett đã khám phá ra giải pháp JavaScript nhanh nhất cho so sánh bằng nhau sâu, điều này có thể hữu ích.
Cách Tạo So Sánh Bằng Nhau Sâu Nhanh Nhất Trong JavaScript
Dưới đây là bài viết của anh ấy: Cách xây dựng hàm “deep equals” nhanh nhất trong JavaScript. Giải pháp trông như sau:
javascript
const addressEquals = Function(
"x",
"y",
`
if (x === y) return true
if (x.street1 !== y.street1) return false
if (x.street2 !== y.street2) return false
if (x.city !== y.city) return false
return true
`
);
Giải pháp này bao gồm việc bọc so sánh của bạn bên trong một chuỗi và đánh giá nó với constructor của Function. Phương pháp này nhanh chóng nhưng không an toàn về kiểu dữ liệu. Nếu hình dạng của đối tượng của bạn thay đổi, bạn có thể quên cập nhật chuỗi so sánh.
Nhưng ngay cả khi không an toàn về kiểu dữ liệu, tôi nghĩ rằng có thể tạo ra một bài kiểm tra kiểu riêng và sử dụng một bộ chuyển đổi kiểu tùy chỉnh để biến đổi đối tượng thành một chuỗi mục tiêu với cùng hình dạng của đối tượng của bạn.
typescript
interface MyObject {...};
const MyObjectComparaisonString = `...` as const
type IsMyDeepEqualsStringCorrect = Expect<Equal<ToDeepEqualsComparaion<MyObjectType>, typeof MyObjectComparaisonString >>
Đây chỉ là một ý tưởng, nhưng nó có thể đủ cho nhu cầu của bạn.
Nếu bạn đã sử dụng một thư viện schema, thư viện này là hoàn hảo cho nhu cầu của bạn!
@traversable
: Giải Pháp Đơn Giản cho Sự Bằng Nhau Sâu với Các Bộ Kiểm Tra Schema
Andrew Jarrett cũng đã tạo ra một thư viện giúp việc kiểm tra sự bằng nhau sâu trở nên đơn giản khi làm việc với các bộ kiểm tra schema. Sử dụng zod
làm ví dụ:
javascript
import { z } from "zod";
import { zx } from "@traversable/zod";
const Address = z.object({
street1: z.string(),
street2: z.optional(z.string()),
city: z.string(),
});
// Tạo hàm so sánh bằng nhau sâu 👇
const addressEquals = zx.deepEqual(Address);
addressEquals(
{ street1: "221B Baker St", city: "London" },
{ street1: "221B Baker St", city: "London" }
); // => true
Khi được sử dụng với một Signal:
javascript
const address = signal(..., { equal: addressEquals });
const streetViewFromAddressResource = resource({ params: address, ... });
Giờ đây, streetViewFromAddressResource
chỉ được gọi khi địa chỉ thực sự thay đổi. Điều này hoạt động với nhiều thư viện schema khác nhau:
- Zod
- JSON Schema
- ArkType
- TypeBox
- Valibot
Đây là một giải pháp sạch sẽ và phát triển cùng với schema của bạn mà không cần cập nhật thủ công.
Lưu ý: Hãy cẩn thận nếu đối tượng/schema của bạn chứa các hàm hoặc ngày tháng, cách chúng được xử lý có thể không chắc chắn.
👉 Nếu bạn thấy bài viết này hữu ích, đừng ngần ngại để lại một 👍 hoặc chia sẻ ý kiến của bạn trong phần bình luận — tôi rất muốn nghe phản hồi của bạn!
Nếu bạn không biết tôi, tôi là Romain Geffrault, và tôi thường xuyên chia sẻ nội dung về Angular/TypeScript/RxJs/Signal. Hãy xem các bài viết khác của tôi và theo dõi tôi trên LinkedIn Romain Geffrault.