Học cách code splitting có thể nâng cao hiệu suất và tốc độ chạy cho ứng dụng React của bạn.
App React của bạn đang chạy quá chậm hay mất quá nhiều thời gian để tải? Nếu đúng, có thể bạn sẽ cần dùng tới kỹ thuật code splitting. Kỹ thuật này rất hiệu quả trong việc cải thiện tốc độ tải và hiệu suất của ứng dụng React. Vậy code splitting là gì? Cách dùng nó như thế nào?
Code Splitting là gì?
Một ứng dụng React điển hình bao gồm hàng chục thành phần và code. Thế nhưng, bạn không cần tải hết chúng ở lần sử dụng đầu tiên. Code splitting đòi hỏi phải tách các phần khác nhau của ứng dụng và chỉ tải chúng khi cần. Điều này hiệu quả hơn nhiều so với việc tải toàn bộ ứng dụng cùng lúc.
Ví dụ một app có 3 trang: homepage, about page, products page. Khi ở trang chủ, việc tải cả about page và products page thật sự vô nghĩa vì bạn không thực sự ở trên những trang đó, ý tưởng code splitting là đảm bảo bạn chỉ tải code cần thiết.
Phần tuyệt nhất của code splitting là bạn có thể trì hoãn việc tải các thành phần cùng các chức năng của nó.
Chức năng Code Splitting: Dùng Dynamic Import
Hãy xem xét tình huống sau. Bạn muốn trang chủ có một nút bấm. Khi click nút bấm, bạn muốn thông báo tổng của 2 cộng 2. Vì vậy, bạn tạo Home.js và xác định chế độ xem trang chủ.
Trong trường hợp này, bạn có hai lựa chọn. Đầu tiên, bạn có thể nhập code để thêm số ở phía trên cùng của file Home.js. Thế nhưng, ở đây phát sinh vấn đề. Nếu nhập hàm này ở phía trên của file, code sẽ tải ngay cả khi bạn chưa click vào nút bấm. Một cách tiếp cận tốt hơn là tải hàm sum() chỉ khi bạn click vào nút bấm đó.
Để đạt được điều này, bạn cần triển khai dynamic import. Điều đó có nghĩa bạn sẽ nhập hàm sum() nội tuyến trong phần tử nút bấm. Đây là code:
export default function Home() {
return (
<div className="Home">
<h1>HomePage</h1>
<button onClick={() => {
import("../sum.js").then((module) => {
alert(module.sum(2, 2))
})
}}
>
Sum both numbers
</button>
</div>
);
}
Lưu ý, trình duyệt này sẽ chỉ tải mô đun sum.js khi bạn click vào nút bấm. Điều này cải thiện thời gian tải của trang chủ.
Các thành phần code Splitting: Dùng React.lazy và Suspense
Bạn có thể tách các thành phần trong React bằng hàm lazy(). Nơi tốt nhất để tiến hành code splitting là bên trong router bởi đây là nơi bạn ánh xạ các thành phần tới các route trong ứng dụng.
Giả sử app của bạn có Home, About và Products. Khi ở Home, việc tải About hay Products là vô nghĩa. Vì thế, bạn cần tách chúng ra khỏi route Home. Code sau minh họa cách đạt được điều đó:
Đầu tiên, nhập các hàm và thành phần cần thiết từ mô đun react và react-router-dom:
import { Routes, Route, Outlet, Link } from "react-router-dom";
import { lazy, Suspense } from "react";
Tiếp theo, nhập các thành phần dynamic bằng hàm lazy():
const Home = lazy(() => import("./components/Home"));
const About = lazy(() => import("./components/About"));
const Products = lazy(() => import("./components/Products"));
Tiếp theo, thiết lập bố cục (menu điều hướng). Dùng <Outlet /> để xuất thành phần tương ứng với route hiện tại (Home, About hoặc Products):
function NavWrapper() {
return (
<>
<nav style={{ display: "flex", gap: "1rem" }}>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/products">Products</Link>
</nav>
<Suspense fallback={<h1>Loading...</h1>}>
<Outlet />
</Suspense>
</>
);
}
Bạn có thể thấy các thành phần được gói trong <Suspense/>. Điều này báo cho React biết rằng mọi thứ bên trong <Outlet/> đều có khả năng tải chậm. Vì thế, phần tử Suspense có thuộc tính fallback. Trong trường hợp đó, giá trị là văn bản text đơn giản báo “Loading…”. Trong khi từng trang được tải, bạn sẽ nhận được thông báo tải trên màn hình.
Cuối cùng, thiết lập route:
export default function App() {
return (
<Routes>
<Route path="/" element={<NavWrapper />}>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/about" element={<About />} />
</Route>
</Routes>
);
}
Giờ khi truy cập homepage này, trình duyệt chỉ tải file Home.js. Tương tự, khi click link About trong menu điều hướng để truy cập trang About, trình duyệt chỉ tải file About.js. Điều này giống với trang Products.
Code Spliting theo điều kiện
Đôi khi, bạn có một số nội dung trên trang chỉ phù hợp với người dùng nhất định. Ví dụ, trên homepage, bạn có một phần chứa dữ liệu admin chỉ dành cho quản trị viên. Nó có thể là bảng quản trị không dành cho người dùng thông thường.
Trong trường hợp này, bạn sẽ không muốn hiện tất cả dữ liệu đó mỗi lần tải. Bạn có thể dùng kỹ thuật code splitting để đảm bảo chỉ hiện thông tin đó cho người dùng là quản trị viên.
Khối code trông sẽ như sau:
import { lazy, Suspense } from "react";
const AdminData = lazy(() => import("./AdminData"));
export default function Home() {
const [isAdmin, setIsAdmin] = useState(false)
return (
<div className="Home">
<h1>HomePage</h1>
<button onClick={() => setIsAdmin(prev => !prev)}>
Toggle Admin
</button>
<Suspense fallback={<h1>Loading...</h1>}>
{isAdmin ? <AdminData /> : <h2> Not the Admin </h2>}
</Suspense>
</div>
);
}
Giờ khi click nút toggle, isAdmin sẽ được đặt sang true. Kết quả, ứng dụng sẽ hiện <AdminData> đang được tải chậm. Thế nhưng, nếu bạn là admin, ứng dụng sẽ không bao giờ tải AdminData.js bởi app này sẽ không cần tới nó.
Các khái niệm code splitting nâng cao
Một kỹ thuật nâng cao mà bạn có thể kích hoạt khi splitting code là chuyển tiếp. Hook useTransition() cho phép bạn thực hiện update khẩn cấp mà sẽ không thay đổi UI cho tới khi chúng hoàn tất cập nhật.
Đầu tiên, nhập hook:
import {useTransition} from "react"
Sau đó, gọi hook trả về isPending và startTransition:
const [isPending, startTransition] = useTransition()
Cuối cùng đóng code để update trạng thái bên trong startTransition():
startTransition(() => {
setIsAdmin((prev) => !prev)
})
Giờ UI thực sự của bạn sẽ không hiện giá trị fallback (nội dung đang tải) cho tới khi trình duyệt kết thúc chuyển tiếp. Điều đó có nghĩa nó sẽ đợi trình duyệt tải toàn bộ dữ liệu admin trước khi cố gắng hiện dữ liệu bất kỳ.
Trên đây là những điều cần biết về cách tăng tốc app React bằng code splitting. Hi vọng bài viết hữu ích với các bạn.