- Việc cung cấp bản dựng React chính xác và tối ưu hóa trình đóng gói (phiên bản sản xuất và phiên bản phân tích hiệu năng) là nền tảng cho bất kỳ công việc cải thiện hiệu năng nghiêm túc nào.
- Việc phân tích hiệu năng bằng React DevTools và theo dõi hiệu năng trình duyệt sẽ giúp bạn phát hiện các lần render không cần thiết, hiệu ứng chậm và các điểm nghẽn trên máy chủ mà bạn có thể nhắm đến để khắc phục.
- Ghi nhớ, tính bất biến và ảo hóa phối hợp với nhau để giảm tần suất hiển thị, thu nhỏ khối lượng công việc mỗi lần hiển thị và giữ cho giao diện người dùng lớn hoạt động mượt mà.
- Phân tách mã, SSR, Web Workers và giám sát liên tục đảm bảo tốc độ tải ban đầu nhanh, tương tác mượt mà và hiệu năng bền vững ở quy mô lớn.
React có thể cho cảm giác cực kỳ nhanh ngay từ khi cài đặt, nhưng khi ứng dụng của bạn phát triển, việc tích lũy những điểm suy giảm hiệu năng nhỏ lại khá dễ xảy ra. Những thứ đó biến giao diện mượt mà thành những con quái vật chậm chạp, ngốn pin. Danh sách dài, các thành phần nặng nề, cấu trúc trạng thái khó hiểu và các bản dựng gỡ lỗi trong môi trường sản xuất đều cộng dồn lại cho đến khi người dùng bắt đầu rời bỏ trang của bạn.
Tin tốt là React đi kèm với một bộ công cụ phong phú để đo lường, hiểu và cải thiện hiệu suất hiển thị.Và hệ sinh thái xung quanh (các công cụ đóng gói, phân tích hiệu năng, thư viện cửa sổ, Web Workers, khung SSR) cung cấp mọi thứ bạn cần để giữ cho giao diện người dùng luôn nhanh nhạy ngay cả ở quy mô lớn. Trong hướng dẫn này, chúng ta sẽ đi sâu vào các công cụ đó, chỉ ra cách chúng kết hợp với nhau và nêu bật một số thủ thuật ít được chú ý mà các nhóm thường bỏ qua nhưng lại vô cùng hữu ích.
Sử dụng bản dựng React phù hợp: dành cho phát triển, sản xuất và phân tích hiệu năng.
Bước kiểm tra hiệu năng đầu tiên và quan trọng nhất đối với bất kỳ ứng dụng React nào là xác minh rằng bạn đang triển khai bản dựng dành cho môi trường sản xuất, chứ không phải bản dựng dành cho môi trường phát triển.Phiên bản dành cho nhà phát triển bao gồm rất nhiều cảnh báo thân thiện, các kiểm tra bổ sung và công cụ hỗ trợ gỡ lỗi, rất tuyệt vời khi lập trình nhưng lại chậm hơn và lớn hơn đáng kể khi sử dụng trong môi trường sản xuất.
Bạn có thể xác nhận phiên bản đang sử dụng bằng tiện ích mở rộng trình duyệt React Developer Tools.Khi bạn mở một trang web sử dụng React, biểu tượng tiện ích mở rộng sẽ có nền tối trong môi trường sản xuất và nền đỏ trong môi trường phát triển. Nếu bạn thấy nền đỏ trên trang web đang hoạt động, cấu hình bundler của bạn đang làm rò rỉ bản dựng sai.
Đối với các dự án được khởi tạo bằng Create React App, việc tạo gói sản phẩm được tối ưu hóa đơn giản chỉ bằng cách chạy tập lệnh xây dựng của bạn., xuất ra một gói được thu nhỏ vào build/ thư mục. Trong quá trình phát triển cục bộ, bạn nên tuân theo npm start (hoặc tương đương) và chỉ chạy bản dựng sản xuất để triển khai hoặc để đánh giá hiệu năng thực tế.
Nếu bạn dựa vào các bản dựng UMD dạng tệp đơn của React và React DOM (ví dụ: trong môi trường không đóng gói)Hãy đảm bảo bạn đã bao gồm các tệp có đuôi mở rộng là... .production.min.jsBất kỳ tệp nào chưa được nén hoặc chưa được sử dụng cho mục đích sản xuất đều chỉ dành cho mục đích phát triển và sẽ tạo ra gánh nặng gỡ lỗi không cần thiết cho người dùng của bạn.
Các công cụ đóng gói: Browserify, Rollup, Brunch và webpack
Các trình đóng gói khác nhau yêu cầu các tinh chỉnh khác nhau để khai thác tối đa các tối ưu hóa sản xuất của React.Nhưng tất cả chúng đều tuân theo cùng một ý tưởng cơ bản: thiết lập môi trường sang chế độ sản xuất, loại bỏ các nhánh chỉ dành cho phát triển và thu nhỏ mã JavaScript thu được.
Với Brunch, phương pháp được khuyến nghị là cài đặt một plugin nén file, chẳng hạn như... terser-brunchSau đó, chạy bản dựng của bạn với cờ sản xuất (ví dụ: với -pCấu hình này đảm bảo rằng các cảnh báo trong quá trình phát triển được loại bỏ và gói cuối cùng được nén mạnh mẽ.
Đối với Browserify, bạn thường kết hợp một vài phép biến đổi theo một thứ tự cụ thể.: trước tiên hãy áp dụng envify trên toàn cầu để tiêm NODE_ENV="production", sau đó áp dụng uglifyify trên toàn cầu để xóa các lệnh nhập khẩu và đường dẫn mã phát triển, và cuối cùng chuyển gói đó qua terser để làm biến dạng và nén mã. Thứ tự các bước rất quan trọng vì mỗi bước chuẩn bị mã cho quá trình biến đổi tiếp theo.
Khi sử dụng Rollup, bạn kết nối ba plugin để tạo ra một bản dựng sản xuất gọn nhẹ.: replace thiết lập môi trường sang chế độ sản xuất. commonjs cho phép đóng gói các mô-đun CommonJS, và terser Thực hiện quá trình thu nhỏ và xử lý mã cuối cùng. Sự kết hợp này tạo ra một gói nhỏ gọn, sẵn sàng cho môi trường sản xuất mà không cần các công cụ hỗ trợ chỉ dành cho nhà phát triển.
Với webpack 4 trở lên, việc bật chế độ sản xuất sẽ tự động kích hoạt nhiều tối ưu hóa, bao gồm cả việc thu nhỏ mã.. Cài đặt mode: 'production' Các dây dẫn trong Terser hoạt động ngầm và cho phép hành vi sản xuất của React miễn là... NODE_ENV phù hợp. Thông thường bạn không cần thêm trình thu nhỏ riêng biệt trừ khi bạn có những yêu cầu rất cụ thể.
Phân tích hiệu năng các bản dựng React
Ngoài các bản dựng phát triển và sản xuất thông thường, React còn cung cấp một bản dựng đặc biệt tập trung vào phân tích hiệu năng.Biến thể này điều chỉnh các công cụ bên trong React để các công cụ như DevTools Profiler có thể thu thập thông tin thời gian rất chi tiết.
Để sử dụng bản dựng phân tích hiệu năng trong môi trường trình duyệt, bạn cần nhập react-dom/profiling thay vì react-dom/client và thường cấu hình một bí danh trình đóng gói để bạn không phải tự tay chỉnh sửa từng lệnh import. Một số framework đã cung cấp sẵn cờ hoặc chế độ để bật/tắt hành vi này cho bạn.
Các phiên bản React trước đây (trước phiên bản 17) dựa vào API User Timing tiêu chuẩn. Để hiển thị các dấu hiệu và số liệu đo lường trong bảng Hiệu suất của trình duyệt. React hiện đại kết hợp những khả năng này với tab Profiler chuyên dụng trong React DevTools để bạn có thể xem chi tiết các thành phần trực tiếp.
Hiểu và đo lường hiệu năng của React
Bạn không thể khắc phục những gì bạn không đo lường, vì vậy việc tối ưu hiệu năng trong React luôn nên bắt đầu bằng việc phân tích hiệu năng.Điều đó có nghĩa là sử dụng các công cụ trình duyệt và các công cụ phân tích hiệu năng dành riêng cho React để xem thời gian thực sự được dành cho việc gì và thành phần nào đang được render lại nhiều hơn mức cần thiết.
Bảng Hiệu suất của Chrome DevTools là cơ sở để bạn hiểu trình duyệt đang hoạt động như thế nào.Việc thực thi JavaScript, các yêu cầu mạng, bố cục, vẽ, độ trễ vòng lặp sự kiện và các dấu vết tùy chỉnh đều được hiển thị trên một dòng thời gian thống nhất. React tích hợp vào chế độ xem này với các bản ghi chuyên biệt hiển thị hoạt động cụ thể của khung.
React hiện đại cung cấp các trình theo dõi Scheduler, Components và Server, đồng bộ với các dấu vết trình duyệt thông thường.Điều này giúp bạn có cái nhìn đồng bộ về các cập nhật mạng, JavaScript và React, cực kỳ hữu ích khi bạn đang tìm nguyên nhân gây giật lag hoặc các sự cố bất thường chỉ xuất hiện khi hệ thống chịu tải.
Lập lịch theo dõi và các giai đoạn kết xuất
Scheduler là một lớp trừu tượng nội bộ của React, có chức năng điều phối công việc ở các mức độ ưu tiên khác nhau.Trong nhật ký hiệu năng, bạn sẽ thấy các nhánh riêng biệt cho các tác vụ chặn (thường là các cập nhật do người dùng thực hiện đồng bộ), các tác vụ chuyển tiếp (các cập nhật giao diện người dùng nền được kích hoạt bởi...). startTransition), Các nhiệm vụ liên quan đến tình trạng chờ đợi và các công việc nhàn rỗi chạy khi không có việc gì khẩn cấp hơn đang chờ xử lý.
Mỗi lượt render đều trải qua một số giai đoạn riêng biệt mà bạn có thể kiểm tra trên dòng thời gian.: một giai đoạn cập nhật (điều gì đã kích hoạt quá trình render), một giai đoạn render (nơi React gọi các component của bạn và xây dựng cây DOM tiếp theo), một giai đoạn commit (nơi DOM bị thay đổi và các hiệu ứng bố cục như...) useLayoutEffect chạy) và giai đoạn hiệu ứng còn lại (trong đó các hiệu ứng thụ động như useEffect (thường chạy sau khi sơn).
Các bản cập nhật theo kiểu xếp tầng — những thay đổi trạng thái được lên lịch trong quá trình hiển thị — là một nguồn gốc kinh điển của các vấn đề hiệu năng tiềm ẩn.Trong quá trình phát triển, React có thể đánh dấu những sự kiện này trên dòng thời gian và thậm chí hiển thị thành phần và phương thức nào đã lên lịch cập nhật bổ sung, giúp bạn tránh các vòng lặp hiển thị không mong muốn hoặc công việc lặp lại.
Theo dõi các thành phần: biểu đồ ngọn lửa cho quá trình kết xuất và hiệu ứng.
Biểu đồ Components hiển thị trực quan thời gian cần thiết để hiển thị từng thành phần (và các thành phần con của nó). Sử dụng biểu đồ ngọn lửa (flamegraph). Khối càng rộng trên biểu đồ, cây con của thành phần đó càng tiêu tốn nhiều thời gian trong quá trình kết xuất.
React cũng hiển thị thời lượng hiệu ứng dưới dạng biểu đồ ngọn lửa riêng biệt. Với bảng màu phản ánh giai đoạn tương ứng trong track Lập lịch, bạn có thể dễ dàng phân biệt thời gian render với thời gian hiệu ứng chỉ bằng một cái nhìn.
Các sự kiện bổ sung như gắn kết, tháo gỡ, kết nối lại và ngắt kết nối xuất hiện dưới dạng chú thích trên các biểu đồ ngọn lửa này.Ví dụ, việc lắp đặt một phần mới của cây hoặc tháo dỡ một phần sẽ được đánh dấu, và một số tính năng như... <Activity> Các thành phần có các dấu hiệu kết nối lại/ngắt kết nối riêng.
Trong môi trường phát triển, khi nhấp vào một mục hiển thị trong phần Components, bạn sẽ thấy các thuộc tính nào đã thay đổi.Điều này vô cùng hữu ích khi bạn đang cố gắng tìm ra các bản render không cần thiết hoặc các đối tượng liên tục thay đổi tham chiếu mà giá trị thực tế không thay đổi.
Theo dõi máy chủ: các yêu cầu và các thành phần máy chủ
Nếu bạn đang sử dụng React Server Components, các công cụ đo hiệu năng cũng có thể giúp phát hiện hành vi phía máy chủ.Một track "Server Requests" tổng hợp các Promise, cuối cùng sẽ cung cấp dữ liệu cho các thành phần máy chủ, bao gồm cả các lệnh gọi đến... fetch hoặc các thao tác hệ thống tập tin không đồng bộ.
React cố gắng nhóm các Promise được tạo trong các hàm hỗ trợ của bên thứ ba vào một span duy nhất. Vì vậy, bạn sẽ thấy một phép toán logic như sau: getUser thay vì một tá cấp thấp fetch Các cuộc gọi. Nhấp vào một span sẽ hiển thị nơi nó được tạo và, nếu có, giá trị đã được giải quyết hoặc lý do từ chối.
Một phần riêng biệt hiển thị thời gian mà cây thành phần máy chủ và các Promise được chờ đợi của chúng mất để thực hiện.Cũng có dạng biểu đồ ngọn lửa. Khi React có thể hiển thị các thành phần máy chủ đồng thời, nó sẽ tạo ra một luồng chính và các luồng song song bổ sung; nếu số lượng luồng đồng thời vượt quá một con số nhất định, công việc bổ sung sẽ được nhóm lại để giữ cho giao diện dễ đọc.
Giảm thiểu các lần render không cần thiết: React.memo, useMemo, useCallback và PureComponent
Một trong những nguyên nhân gây hao phí hiệu năng lớn nhất và phổ biến nhất trong các ứng dụng React là việc render lại không cần thiết.Mỗi khi một thành phần cha được cập nhật, các thành phần con của nó sẽ tự động được render lại, ngay cả khi các tham số đầu vào (props) của chúng giống hệt nhau và DOM đầu ra thực tế không thay đổi.
React cung cấp một số công cụ để giảm thiểu công việc lãng phí này: React.memo đối với các thành phần chức năng, React.PureComponent đối với các thành phần của lớp và useMemo/useCallback các điểm kết nối để ổn định các giá trị được truyền xuống dưới dạng propsNhững giải pháp này không thể tự động khắc phục mọi vấn đề về hiệu năng, nhưng nếu sử dụng một cách hợp lý, chúng có thể tạo ra sự khác biệt rất lớn.
React.memo Nó bao bọc một thành phần chức năng và bỏ qua việc render lại khi các props của nó bằng nhau một cách nông cạn với các props trước đó.Điều này đặc biệt hữu ích khi một thành phần được hiển thị thường xuyên với cùng các props, có logic hiển thị phức tạp hoặc bạn có bằng chứng từ Profiler cho thấy đó là điểm nghẽn.
Khi bạn ghi nhớ trạng thái của một component, bạn cũng cần đảm bảo rằng các props của nó không làm thay đổi danh tính một cách không cần thiết.Việc tạo một đối tượng mới hoặc hàm nội tuyến bên trong JSX cha trong mỗi lần render sẽ làm mất hiệu lực phép so sánh nông và buộc phần tử con phải render lại, ngay cả khi dữ liệu logic giống nhau.
Đây là lúc useMemo và useCallback mời vào: useMemo Ổn định các giá trị đối tượng hoặc mảng được tạo ra từ trạng thái khác để chúng chỉ thay đổi khi các phần phụ thuộc của chúng thay đổi, và useCallback Cung cấp các tham chiếu hàm ổn định cho các hàm gọi lại được truyền vào các phần tử con được ghi nhớ.
Các thành phần lớp: shouldComponentUpdate và React.PureComponent
Về bản chất, hầu hết các tối ưu hóa hiển thị trong React đều xoay quanh việc kiểm soát xem liệu... shouldComponentUpdate trả về true hoặc falseViệc triển khai mặc định luôn trả về true, có nghĩa là bất kỳ thay đổi nào về prop hoặc state đều kích hoạt quá trình render và reconciliation cho component đó và cây con của nó.
Bằng cách ghi đè shouldComponentUpdateBạn có thể bỏ qua bước xử lý cho các nhánh cây không cần cập nhật.Nếu bạn trả về false, React sẽ không gọi phương thức đó. render() Đối với thành phần đó hoặc bất kỳ hậu duệ nào của nó, nó thậm chí sẽ không so sánh các nút DOM ảo mới và cũ cho phần đó của cây.
Hãy xem xét một cây thành phần nhỏ trong đó một số nút trả về giá trị false từ shouldComponentUpdateReact có thể bỏ qua hoàn toàn việc duyệt qua các nhánh đó, trong khi các nút khác mà phương thức trả về true sẽ được xử lý đầy đủ. Cuối cùng, chỉ những nút có kết quả hiển thị thực sự thay đổi mới gây ra sự thay đổi DOM.
Vì việc viết tùy chỉnh shouldComponentUpdate Logic mang tính lặp lại, React có thể vận chuyển hàng hóa. React.PureComponent, phương thức này thực hiện so sánh nông (shallow comparison) giữa các props và state hiện tại và trước đó. Nếu không có gì thay đổi ở mức độ nông, React có thể bỏ qua việc render lại component của lớp đó một cách an toàn.
Tính bất biến và lý do tại sao phép so sánh nông cạn có thể thất bại
So sánh nông cạn giả định rằng nếu một giá trị thay đổi, tham chiếu của nó cũng sẽ thay đổi — một giả định sẽ bị phá vỡ ngay khi bạn thay đổi trực tiếp các mảng hoặc đối tượng hiện có.Đây là một nguồn gây lỗi kinh điển khi kết hợp các tối ưu hóa dựa trên tính bất biến với các cấu trúc dữ liệu có thể thay đổi.
Hãy tưởng tượng một ListOfWords thành phần nhận words mảng và hiển thị chúng dưới dạng các phần tử được phân tách bằng dấu phẩy., kết hợp với cha mẹ WordAdder thành phần thêm một từ mới vào cùng một mảng đó. Nếu ListOfWords kéo dài PureComponentViệc so sánh nông (shallow comparison) sẽ thấy cùng một tham chiếu mảng và cho rằng không có gì thay đổi, do đó giao diện người dùng sẽ không cập nhật.
Cách khắc phục là tránh thay đổi trực tiếp các props hoặc state, thay vào đó hãy tạo các mảng hoặc đối tượng mới khi dữ liệu thay đổi.. Thay vì words.push(newWord)bạn sẽ sử dụng words.concat(newWord) hoặc cú pháp trải rộng [...words, newWord]Điều này tạo ra một tham chiếu mới cho mảng và kích hoạt các cập nhật chính xác.
Nguyên tắc tương tự cũng áp dụng cho các vật thể.thay vì phân công lại colormap.right = 'blue' Trên một đối tượng hiện có, bạn sẽ trả về một đối tượng mới bằng cách sử dụng Object.assign({}, colormap, { right: 'blue' }) hoặc cú pháp trải rộng đối tượng { ...colormap, right: 'blue' }Điều này đảm bảo rằng sự so sánh hời hợt sẽ nhận ra một tham chiếu mới và công nhận sự thay đổi.
Khi dữ liệu lồng nhau quá sâu, việc duy trì tính bất biến bằng tay có thể trở nên khó quản lý.Các thư viện như Immer hoặc immutability-helper cho phép bạn viết mã trông có vẻ mệnh lệnh và có thể thay đổi, trong khi bên trong lại tạo ra các cấu trúc bất biến mới, điều này rất phù hợp với... PureComponent và React.memo.
Ảo hóa các danh sách dài và giao diện người dùng phức tạp
Việc hiển thị hàng trăm hoặc hàng nghìn nút DOM cùng một lúc là một trong những cách nhanh nhất làm giảm hiệu năng của React.Đặc biệt là trên các thiết bị cấu hình thấp hoặc khi kết hợp với bố cục và hình ảnh phức tạp. Ngay cả với việc đồng bộ hóa hiệu quả, chỉ riêng việc lưu trữ quá nhiều nút trong bộ nhớ và trên màn hình cũng đã tốn kém.
Kỹ thuật cửa sổ hóa, hay ảo hóa danh sách, giải quyết vấn đề này bằng cách chỉ hiển thị phần danh sách hiện đang hiển thị trong khung nhìn.Khi người dùng cuộn trang, React sẽ thêm các mục mới vào khung nhìn và loại bỏ các mục bị cuộn ra ngoài, giữ cho số lượng hàng được hiển thị gần như không đổi.
Các thư viện phổ biến như react-window và react-virtualized Cung cấp các thành phần có thể tái sử dụng cho danh sách, lưới và bảng. Chúng triển khai các chiến lược ảo hóa hiệu quả. Chúng xử lý các phép toán về việc hiển thị các mục nào, kích thước, cuộn các vùng chứa và thậm chí cả hành vi tải vô hạn.
Việc thiết lập ảo hóa thường bao gồm ba bước.: lựa chọn thành phần phù hợp (ví dụ, FixedSizeList cho các hàng đồng đều hoặc VariableSizeList (đối với chiều cao thay đổi linh hoạt), giúp thùng chứa có chiều cao cố định. overflow: scrollvà chỉ hiển thị thành phần mục mà thư viện yêu cầu, thường được ghi nhớ bằng cách sử dụng React.memo để tránh việc phải render lại không cần thiết.
Nếu được thực hiện tốt, ảo hóa giúp duy trì hiệu suất cuộn mượt mà và mức sử dụng bộ nhớ thấp ngay cả với các tập dữ liệu khổng lồ.Các ứng dụng thực tế đã sử dụng kỹ thuật này để duyệt hiệu quả các bộ sưu tập khổng lồ — đánh giá âm nhạc, danh mục thương mại điện tử, hộp thư đến — mà không làm giao diện người dùng bị chậm lại.
Khả năng truy cập đòi hỏi sự chú ý đặc biệt hơn đối với các danh sách ảo hóa.Bạn cần đảm bảo chức năng điều hướng bằng bàn phím hoạt động, tiêu điểm được quản lý chính xác khi các mục được gắn kết và gỡ bỏ, và trình đọc màn hình có đủ ngữ cảnh thông qua các thuộc tính ARIA để hiểu phần danh sách hiện đang hiển thị.
Quản lý trạng thái, DOM ảo và cấu trúc thành phần
DOM ảo thường bị hiểu nhầm là giải pháp thần kỳ, nhưng thực chất nó chỉ là một lớp so sánh thông minh.React duy trì một bản sao lưu giao diện người dùng trong bộ nhớ và so sánh cây DOM mới với cây DOM cũ để quyết định những thao tác DOM nào là thực sự cần thiết.
Ngay cả với hiệu quả đó, mỗi lần render và so sánh vẫn tốn thời gian, vì vậy mục tiêu của bạn là giảm thiểu tần suất cần phải render lại các cây con lớn.Đây là nơi mà việc quản lý trạng thái, ranh giới thành phần và các chiến lược ghi nhớ giao nhau.
Trước tiên, hãy chọn một chiến lược quản lý trạng thái phù hợp với độ phức tạp của ứng dụng.. Trạng thái React cục bộ (useState, useReducer(Nó nhỏ gọn và đơn giản đối với các thành phần nhỏ, trong khi các thư viện như Redux hoặc các store nhẹ như Zustand có thể tập trung hóa trạng thái toàn cục phức tạp hơn với các mô hình đăng ký được tối ưu hóa.)
Thứ hai, hãy cấu trúc trạng thái của bạn sao cho dữ liệu liên quan được nhóm lại một cách hợp lý.Đôi khi điều đó có nghĩa là hợp nhất nhiều cái. useState Các lệnh gọi đến một đối tượng duy nhất giúp cho các cập nhật được nhất quán; trong các trường hợp khác, việc tách trạng thái để các vấn đề độc lập không buộc nhau phải vẽ lại sẽ hiệu quả hơn.
Khi cập nhật trạng thái được suy ra từ các giá trị trước đó, hãy luôn sử dụng các cập nhật hàm. như là setCount(prev => prev + 1)và duy trì tính bất biến bằng cách sao chép mảng và đối tượng thay vì thay đổi chúng trực tiếp. Điều này dẫn đến hành vi dễ dự đoán và hoạt động tốt với kỹ thuật ghi nhớ (memoization) và PureComponents.
Một nguyên tắc hữu ích là hãy giữ cho phạm vi toàn bang càng gần với phạm vi địa phương càng tốt.Giá trị trạng thái càng được lưu trữ ở vị trí cao trong cây cấu trúc, thì càng nhiều thành phần sẽ được render lại mỗi khi giá trị đó thay đổi. Việc đẩy trạng thái xuống các thành phần thực sự sử dụng nó sẽ hạn chế phạm vi ảnh hưởng của mỗi lần cập nhật.
Cuối cùng, hãy chia các thành phần lớn thành các phần nhỏ hơn, tập trung hơn, mà các yếu tố phụ trợ của chúng ít khi thay đổi.Các thành phần lá được ghi nhớ với các thuộc tính ổn định giúp giảm lượng DOM ảo mà React cần so sánh và rút ngắn đường dẫn xuống mức tối thiểu các cập nhật DOM.
Phân tách mã, tải lười biếng và tải tài nguyên tốt hơn
Kích thước gói JavaScript là một yếu tố chính gây ra hiệu suất kém, đặc biệt là trên mạng di động.Nếu gói React của bạn mất vài giây để tải xuống và phân tích cú pháp, người dùng sẽ rời trang trước khi họ kịp nhìn thấy giao diện người dùng đẹp mắt của bạn.
Chia mã với React.lazy và Suspense giúp tiết kiệm chi phí bằng cách tải các linh kiện theo yêu cầu thay vì vận chuyển toàn bộ mọi thứ ngay từ đầu.Thay vì đóng gói mọi tính năng vào gói dữ liệu ban đầu, bạn nhập động các thành phần chỉ cần thiết cho các tuyến đường hoặc tương tác cụ thể.
Một chiến lược phổ biến là chia nhỏ tuyến đường.Trong đó, mỗi trang là một khối riêng biệt và chỉ được tải khi người dùng điều hướng đến nó. Bạn có thể tiến xa hơn và chia nhỏ các thành phần tính năng lớn hoặc các bảng ít được sử dụng, miễn là bạn bao bọc chúng trong Suspense với giao diện người dùng dự phòng phù hợp.
Tải chậm cũng áp dụng cho hình ảnh.. Thêm loading="lazy" đến <img> Thẻ (tags) trì hoãn việc tải các hình ảnh nằm dưới màn hình cho đến khi chúng hiển thị trong khung hình, giúp tiết kiệm băng thông và tăng tốc độ hiển thị ban đầu. Đối với các hiệu ứng nâng cao hơn, có thể sử dụng các thư viện như... react-lazy-load-image-component Hỗ trợ các vùng giữ chỗ bị làm mờ và tải dần dần.
Khi triển khai chia tách mã, điều quan trọng là phải cân bằng giữa kích thước khối mã và trải nghiệm người dùng.Việc chia nhỏ quá mức có thể tạo ra quá nhiều yêu cầu nhỏ, trong khi chia nhỏ không đủ sẽ dẫn đến gói dữ liệu ban đầu quá nặng. Việc có các phương án dự phòng tốt và các giới hạn lỗi xung quanh các thành phần lười biếng là rất cần thiết để các yêu cầu mạng bị lỗi không làm sập toàn bộ ứng dụng.
Kết xuất phía máy chủ, các thành phần máy chủ React và các hành động máy chủ
Kết xuất phía máy chủ (SSR) hiển thị ứng dụng React của bạn trên máy chủ và gửi HTML đến máy khách, điều này có thể cải thiện đáng kể hiệu suất cảm nhận và SEO.Người dùng sẽ thấy nội dung hữu ích nhanh hơn, và các công cụ tìm kiếm có thể lập chỉ mục trang của bạn một cách đáng tin cậy hơn.
Các framework như Next.js giúp việc xử lý SSR và HTML truyền tải dữ liệu trở nên khả thi đối với các ứng dụng hàng ngày.Bạn lấy dữ liệu trên máy chủ, hiển thị các thành phần thành HTML—đôi khi thậm chí dưới dạng luồng—và sau đó cập nhật mã HTML đó trên máy khách để nó trở nên tương tác.
Ngoài SSR truyền thống, React Server Components còn đẩy nhiều logic giao diện người dùng hơn về phía máy chủ.Điều này cho phép bạn hiển thị các thành phần mà không bao giờ được gửi đến máy khách. Nhờ đó, kích thước gói dữ liệu phía máy khách có thể được giảm đáng kể và việc đơn giản hóa quá trình lấy dữ liệu, vì các thành phần phía máy chủ có thể gọi trực tiếp cơ sở dữ liệu hoặc API.
Server Actions mở rộng ý tưởng này bằng cách cho phép bạn định nghĩa các hàm chạy trên máy chủ nhưng được kích hoạt từ các thành phần phía máy khách.Điều này giúp loại bỏ rất nhiều điểm cuối REST lặp đi lặp lại hoặc trình xử lý API tùy chỉnh và có thể đơn giản hóa cách bạn xử lý các thay đổi dữ liệu, gửi biểu mẫu và các hoạt động có trạng thái khác.
Khi được sử dụng cùng nhau, SSR, các thành phần máy chủ và các hành động máy chủ sẽ cung cấp cho bạn một loạt các chiến lược hiển thị.Nội dung quan trọng có thể được truyền tải nhanh chóng từ máy chủ, logic phức tạp không cần xử lý trên phía máy khách, và môi trường chạy React sẽ kết nối mọi thứ lại với nhau thành một trải nghiệm người dùng mạch lạc.
Giảm tải công việc nặng nhọc bằng Web Workers
Ngay cả cây React được tối ưu hóa tốt nhất cũng sẽ bị giật lag nếu bạn chạy các tác vụ nặng về CPU trên luồng chính.Các phép tính tốn kém sẽ chặn quá trình hiển thị, làm chậm việc xử lý sự kiện và khiến ứng dụng của bạn hoạt động không mượt mà.
Web Workers cung cấp một cách để chuyển các tác vụ nặng nề đó sang luồng nền.Bạn gửi dữ liệu đến bộ xử lý, để nó thực hiện các phép tính hoặc xử lý các tập dữ liệu lớn, sau đó nhận lại kết quả thông qua truyền thông điệp, giúp luồng chính có thể tập trung vào cập nhật giao diện người dùng.
Các công việc điển hình của Web Workers bao gồm xử lý dữ liệu, xử lý hình ảnh, phân tích thời gian thực hoặc mô phỏng phức tạp.Ví dụ, các trò chơi được xây dựng bằng nền tảng web thường ủy thác logic cốt lõi của trò chơi cho một tiến trình con trong khi luồng chính được dành riêng cho việc hiển thị và xử lý đầu vào.
Việc tích hợp worker với React bao gồm việc tạo một tập tin script riêng biệt, lắng nghe các sự kiện. onmessage bên trong trình xử lý và đăng tin nhắn từ các thành phần của bạnTrong thành phần này, bạn khởi tạo worker và gửi các đầu vào cho nó. postMessage và cập nhật trạng thái khi nó phản hồi, lý tưởng nhất là dọn dẹp tiến trình xử lý khi thành phần bị gỡ bỏ.
Các thư viện như Comlink, workerize hoặc các plugin bundler có thể đơn giản hóa mô hình này. bằng cách trừu tượng hóa việc truyền thông điệp cấp thấp và cung cấp cho bạn một API giống như việc gọi các hàm bất đồng bộ, điều này giúp dễ hiểu hơn trong một codebase React.
Các chỉ số quan trọng về trình duyệt và người dùng cần theo dõi
Ở cấp độ cao hơn, hiệu suất tổng thể của trang web thường được theo dõi bằng các chỉ số tập trung vào người dùng. Chẳng hạn như First Contentful Paint (FCP), Largest Contentful Paint (LCP) và Time to Interactive (TTI). Những chỉ số này cho bạn biết người dùng nhìn thấy nội dung nhanh như thế nào và họ có thể tương tác với nội dung đó sớm ra sao.
Các ứng dụng React tốt thường hướng đến thời gian phản hồi đầu tiên (FCP) dưới khoảng 1.8 giây, thời gian phản hồi cuối (LCP) dưới khoảng 2.5 giây và thời gian phản hồi tổng thể (TTI) dưới 4 giây trên các thiết bị thông thường.Tuy nhiên, ngưỡng chính xác có thể khác nhau tùy theo dự án. Nếu bạn liên tục vượt quá các con số đó, đó là dấu hiệu cho thấy các gói dữ liệu, chiến lược hiển thị hoặc thời gian phản hồi của máy chủ cần được cải thiện.
Các công cụ như Lighthouse, WebPageTest và bảng Hiệu suất của Chrome giúp bạn đo lường các chỉ số này trong môi trường thử nghiệm mô phỏng.Để có được cái nhìn sâu sắc về thực tế, các công cụ Giám sát Người dùng Thực (RUM) như SpeedCurve, Datadog, LogRocket hoặc Sentry theo dõi các phiên người dùng thực tế và liên kết các trải nghiệm chậm với các thay đổi trong mã nguồn.
API Profiler của React tích hợp gọn gàng với hình ảnh này.Bạn có thể quấn các phần của cây thông Noel bằng... <Profiler>Nó ghi lại các lần hiển thị chậm và liên hệ chúng với các luồng người dùng cụ thể. Khi được sử dụng cùng với việc giám sát máy chủ và mạng, điều này cung cấp cho bạn cái nhìn toàn diện về hiệu suất từ đầu đến cuối.
Quy trình làm việc nhóm thực tế để tối ưu hiệu năng
Trong các dự án thực tế, việc tối ưu hiệu năng đạt hiệu quả tốt nhất khi được coi là một quy trình làm việc lặp đi lặp lại chứ không phải là một thao tác dọn dẹp một lần duy nhất.Một quy trình bốn giai đoạn đơn giản—xác định, điều tra, thực hiện, xác nhận—giúp ngăn ngừa các tối ưu hóa nhỏ ngẫu nhiên và tập trung nỗ lực vào những vấn đề quan trọng.
Việc xác định nghĩa là sử dụng các công cụ phân tích hiệu suất, số liệu và báo cáo người dùng để tìm ra các triệu chứng cụ thể. Ví dụ như tốc độ tải trang chậm, tốc độ khung hình thấp hoặc tỷ lệ người dùng bỏ ngang cao trong một số quy trình nhất định. Bạn cần những vấn đề có thể đo lường được, chứ không phải cảm giác chủ quan.
Cuộc điều tra đi sâu vào nguyên nhân gốc rễ.Có thể một trang chứa hàng tá iframe ẩn, có thể một thành phần cụ thể được render lại quá thường xuyên hoặc một thư viện nhà cung cấp khổng lồ đang được tải trên mọi tuyến đường. Trong trường hợp này, bạn cần dựa nhiều vào React DevTools Profiler và dòng thời gian của Chrome.
Triển khai là giai đoạn áp dụng các biện pháp khắc phục mục tiêu.—Ghi nhớ trạng thái hoạt động của một thành phần, ảo hóa một danh sách dài, chia nhỏ một gói dữ liệu, chuyển tải công việc cho Web Worker, hoặc bật SSR cho một số trang nhất định. Mỗi thay đổi cần đủ nhỏ để dễ hiểu.
Xác nhận là bước cuối cùng và thường bị bỏ qua nhiều nhất.Bạn chạy lại các kịch bản phân tích hiệu năng và kiểm tra bảng điều khiển số liệu để đảm bảo rằng thay đổi đó thực sự cải thiện các con số và không gây ra sự suy giảm hiệu năng ở những nơi khác trong hệ thống.
Khi kết hợp đúng cách xây dựng React, ghi nhớ hợp lý, thực hành trạng thái bất biến, ảo hóa danh sách, phân tách mã chiến lược, SSR, Web Workers và đo lường liên tục, bạn sẽ có được các ứng dụng React duy trì tốc độ và khả năng phản hồi nhanh ngay cả khi chúng trở nên phức tạp hơn.Các kỹ thuật nêu trên không phải là về việc tinh chỉnh quá sớm mà là về việc xây dựng một kiến trúc trong đó hiệu năng trở thành kết quả tự nhiên thay vì là một cuộc chiến không ngừng nghỉ.


