Ở bài viết trước, chúng ta đã tìm hiểu về cách liên kết các file là export và import file. Trong bài viết này, chúng ta sẽ tìm hiểu về cách sử dụng code splitting và lazy load trong React component thông qua phương thức dynamic import(), React.lazy và React.Suspense như thế nào nhé!
Code splitting là gì?
Hầu hết các file trong ứng dụng React sẽ được bundle (đóng gói) bằng các công cụ như Webpack, Rollup hoặc Browserify. Đóng gói là quá trình xử lý những files đã được import và kết hợp chúng thành một file duy nhất.
Nếu một ứng dụng có kích thước lớn,file đóng gói sẽ phình to theo, đặc biệt, khi chúng ta sử dụng thêm các thư viện bên thứ 3 (third party library). Để tránh việc nhận một gói bundle lớn, chúng ta cần phải chia nhỏ file bundle.
Code splitting là một tính năng hỗ trợ tạo ra nhiều file bundle nhỏ có thể load một cách tự động tại thời điểm runtime.
Ưu điểm khi chia nhỏ code:
- Cho phép trình duyệt của người dụng tải toàn bộ ứng dụng trong một lần, họ có thể chuyển bất cứ trang nào mà không cần load lại trang
- Trình duyệt không cần required hoặc import bất kì file nào nữa vì tất cả chúng đã ở trong file bundle.
Dynamic import trong React
Một trong những cách chia nhỏ code trong React là sử dụng dynamic import (import động) với cú pháp import().
Gọi import() để load một module dựa trên hàm Javascript Promises. Hàm này trả về một promise nếu cho phép load module đó, hoặc rejected nếu không được load.
Xét ví dụ sau:
import Login from "Pages/Login.js";
import Home from "Pages/Home.js";
import About from "Pages/About.js";
import Contact from "Pages/Contact.js";
import Blog from "Pages/Blog.js";
import Shop from "Pages/Shop.js";
Đoạn code trên sử dụng static import thông thường (import tĩnh), đây là một synchronous (đồng bộ). Một webpack chạy qua cú pháp trên sẽ đóng gói tất cả các file trên chung với nhau.
Nhưng dynamic import thì khác:
const module = await import('/modules/myCustomModule.js');
Dynamic import là một asynchronous (bất đồng bộ). Một webpack chạy qua cú pháp này sẽ bắt đầu chia nhỏ để đóng gói các file trên thành các file bundle nhỏ.
Lazy load là gì?
Lazy load là một design pattern để tối ưu hóa cho ứng dụng. Khi ta vào một trang web, không phải tất cả nội dung sẽ hiển thị hết lên trình duyệt, mà sẽ hiển thị từ từ với loading. Tùy vào lúc chuyển trang trên trình duyệt, ứng dụng sẽ load những component người dùng cần, sau đó sẽ tiếp tục loading những giá trị tiếp theo. Điều này sẽ giúp trang web sẽ chuyển động mượt mà và nhanh chóng hơn, giúp tăng trải nghiệm của người dùng.
React có hai tính năng để ứng dụng ý tưởng lazy load này cho component là React.lazy() và React.Suspense()
Sử dụng React.Lazy()
React.lazy() là hàm cho phép chúng ta render một dynamic import như một component bình thường.
React.lazy() nhận vào một function, function đó phải trả về một promise bởi cú pháp import() để load component.
React.lazy() trả về một promise, sau đó phân giải thành một React component được export default.
Dưới đây là một static import:
import Login from './Login';
Sau đó ta thử đổi sang thành dynamic import sử dụng React.lazy():
const Login = React.lazy(() => import('./Login'));
Trong lần tải đầu tiên, file bundle chứa OtherComponent này sẽ tự động tải.
React.Suspense()
React component được render bởi React.lazy() nên được sử dụng trong một component Suspense của React. Nó cho phép chúng ta định nghĩa các nội dung trong khi chờ đợi các lazy component được load. Ví dụ như loading text, loading element, images…
Component Suspense có thuộc tính fallback có thể render bất kì React element nào chúng ta muốn trong khi chờ đợi component được tải lên. Ta có thể đặt Suspense component bất kì nời nào trên một lazy component.
Một component Suspense có thể bọc một hoặc nhiều lazy component.
import React, { Suspense } from 'react';
const UserList = React.lazy(() => import('./UserList'));
const AdminList = React.lazy(() => import('./AdminList'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<UserList />
<AdminList />
</section>
</Suspense>
</div>
);
}
Error boundary (Thông báo lỗi)
Từ ví dụ trên, nếu component UserList hoặc AdminList không thể tải lên do lỗi mạng hoặc một lỗi nào đó, Error Boundary sẽ hiển thị lỗi đó. Ta có thể tự định nghĩa cách hiển thị lỗi đó như thế nào trên trình duyệt.
Chúng ta có thể sử dụng bằng cách bọc ngoài component Suspense để hiển thị lỗi. Khi có lỗi, thay vì React “ném ra” lỗi và ngừng ứng dụng thì Error boundary sẽ hiển thị lỗi như một content của component, giúp ứng dụng chạy không bị gián đoạn.
Ví dụ, tạo một bộ đếm từ một đến 4, nếu đếm đến 5 thì sẽ ném ra lỗi:
class ECounter extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(({ counter }) => ({
counter: counter + 1,
}));
}
render() {
if (this.state.counter === 5) {
throw new Error("I crashed!");
}
return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
}
}
Sau đó, tạo component ErrorBoundary để hứng lỗi của component ECounter hiển thị lên trình duyệt:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo,
});
}
render() {
if (this.state.errorInfo) {
// Error path
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: "pre-wrap" }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
Cuối cùng, ta bọc ErrorBoundary bên ngoài component ECount như sau:
function App() {
return (
<div>
<hr />
<ErrorBoundary>
<p>Nếu bộ đếm đếm đến 5 sẽ lỗi !!</p>
<ECounter />
</ErrorBoundary>
</div>
);
}
Nếu không có Error Boundary hứng lỗi thì React sẽ show lỗi như sau và không thế thực hiện những hành động khác:
Nhưng khi lỗi được bao bởi Error Boundary thì lỗi sẽ hiển thị như một nội dung của component, không ảnh hưởng đến các action của component khác:
Lazy load bằng Router (bộ định tuyến)
Đây là một cách để phân chia code trong ứng dụng khi bạn không biết phân chia chúng như thế nào.
Chúng ta có thể bắt đầu chia code theo đường dẫn (router) để tránh mất thời gian tải laị các component khi chuyển trang.
Ví dụ:
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
Hy vọng những thông tin trên sẽ giúp chúng ta hiểu hơn về cách sử dụng code splitting và lazy load trong React. Cám ơn các bạn đã ghé thăm bài viết nhé!
Bài viết có tham khảo thông tin từ link: https://reactjs.org/docs/code-splitting.html