Routing với webapp React: Không dùng và có dùng thư viện
React là một thư viện JavaScript ở Front-end nên khi nói đến routing, chúng ta phải hiểu rằng đây là client-side routing.
Routing trong React là việc lựa chọn component nào nên được hiển thị, tạo cho người dùng cảm giác họ đang “di chuyển” giữa các trang giao diện. Mặc dù chúng ta có thể tự viết cấu trúc render có điều kiện kèm với History API để làm điều này nhưng trong thực tế người ta thường xài thư viện react-router với nhiều chức năng routing đa dạng hơn.
Thử tự code chức năng routing
Trước khi học cách dùng thư viện, chúng ta hãy thử tự viết chức năng routing để hiểu về nền tảng một chút. Hãy cùng xem ví dụ nhỏ dưới đây:
Hãy bắt đầu từ file App.js
:
- Component
<RoutingProvider>
ở dòng 15 là một Context để các Route truy cập dữ liệu dùng chung. - Các component
<RouteLink>
từ dòng 20 đến 22 dùng để thực hiện client-side routing, nó có các cơ chế ngăn không cho trình duyệt gọi lên server để thực hiện server-side routing. - Các component
<Route>
từ dòng 25 đến 33 làm nhiệm vụ so khớp (match) để đưa ra quyết định có nên hiển thị nhánh con của nó hay không.
Bây giờ hãy chuyển qua file router.js
:
- Component
<RouteLink>
xử lý sự kiện click vào link như sau (dòng 31 đến 33):event.preventDefault()
bảo trình duyệt đừng làm gì hết, để tao tự xử.- Hàm
navigate()
thêm một dòng vào history để hỗ trợ di chuyển tới lui bằng nút Back và Forward trên trình duyệt. - Hàm
reRoute()
chỉ là một mánh nhỏ, ép component<RoutingProvider>
phải render lại toàn bộ app, khiến cho các componentRoute
ở bên trong phải tiến hành chọn lựa lại giao diện cần hiển thị.
- Component
<Route>
cũng đơn giản không kém, nó chỉ đơn giản là so khớp proppath
của nó với phần path trong URL của trình duyệt để quyết định xem có nên hiển thị children của nó hay không.currentPath.match(new RegExp(path + '(?:\\/|$)'))
so khớp bằng biểu thức chính quy (regular expression), đảm bảo path “/tech” không khớp với path “/tech-support”.
- Dòng 64
window.addEventListener('popstate', reRender)
lắng nghe sự kiện người dùng nhấn nút Back và Forward trên trình duyệt web để thực hiện lại việc routing.- Code nhúng trong IDE online không hoạt động đúng với nút Back và Forward. Hãy mở link sau ở một tab mới https://71rbj.csb.app/ để chạy thử ví dụ này.
Những chức năng sau đây cần trong thực tế nhưng tự code thì rất mất thời gian:
- Tô sáng menu item tương ứng với trang hiện hành.
- So khớp với wildcard
*
,+
hay?
.Ví dụ route/tech*
sẽ khớp với URL path “/tech”, “/techlist”, “/tech-support”; trong khi đó route/tech+
chỉ khớp với “/techlist” và “/tech-support”. - Tham số route (route parameter). Ví dụ route
/articles/:pageIndex/:pageSize
sẽ khớp với URL path “/articles/2/50”, đồng thời component bên trong route sẽ nhận được props chứa object{ pageIndex: 2, pageSize: 50 }
. - Route lồng nhau (nesting routes).
- Chuyển đổi qua lại giữa hash-based và history-based routing.
- v.v. và v.v.
Hướng dẫn sử dụng react-router
react-router không phải là thư viện duy nhất, cũng không cùng nhà phát hành với React nhưng nó phổ biến nhất trong cộng đồng lập trình viên React. Thư viện này chỉ cung cấp những logic xử lý cơ chế routing, ứng dụng React Web cần cài đặt thư viện react-router-dom còn ứng dụng di động React Native cần cài đặt react-router-native để tích hợp sâu hơn các chức năng routing. Cả hai thư viện này đều có dependency là react-router nên chỉ cần cài đặt một trong hai thư viện này thì cũng tự có react-router luôn.
Bây giờ chúng ta hãy cài đặt thư viện react-router-dom (npm install react-router-dom
hoặc yarn add react-router-dom
) rồi sửa code của ví dụ ở trên một chút:
Như vậy về chức năng ở tầng giao diện hầu như không thay đổi gì hết, kể cả nút Back và Forward, điểm khác biệt duy nhất là khi refresh hoặc copy-paste URL thì các bạn sẽ thấy menu item của trang hiện hành được tô sáng.
Các loại Router
Mỗi Router trong thư viện react-router-dom đại diện cho mỗi cơ chế routing khác nhau, các bạn có thể sửa dòng số 3 trong file App.js
để thử nghiệm mỗi loại. Sau khi sửa và save code xong, hãy truy cập trực tiếp trang này để chức năng Back/Forward chạy đúng https://1ie1x.csb.app/.
BrowserRouter
Router này sử dụng History API giúp cho URL trong ô địa chỉ trình duyệt trông rất tự nhiên. Đây là loại Router phổ biến và khuyến khích các bạn nên xài, trừ phi yêu cầu của dự án bắt phải hỗ trợ các trình duyệt web cũ không hỗ trợ History API.
HashRouter
Đây là cơ chế routing dựa trên URL fragment (còn gọi là phần hash), tức là phần text nằm sau ký tự #
của URL. Ưu điểm của nó là chạy được trên mọi loại trình duyệt, thậm chí hỗ trợ luôn nút Back và Forward.
MemoryRouter
Là cơ chế mà react-router sẽ tự quản lý trạng thái routing trong bộ nhớ chứ không phụ thuộc vào URL của trình duyệt. Cơ chế routing này đã từng phổ biến trong quá khứ, khi người ta chưa sáng tạo ra ý tưởng routing bằng URL hash. Ngày nay loại router này phù hợp cho các ứng dụng React không chạy trên trình duyệt web, ví dụ như React Native.
Component <Route>
Hiển thị component theo route
Component này làm nhiệm vụ so khớp và ra quyết định có nên hiển thị component mà nó đại diện hay không, có 3 cách để gắn một component vào <Route>
Dùng prop component
nhận vào một React ElementType (kiểu của PropTypes), ví dụ <Route component={HomePage} path="..." />
, nếu route được so khớp, component HomePage sẽ được hiển thị bằng hàm React.createElement
.
Dùng prop render
nhận vào một function, function này phải trả về một React Node, ví dụ <Route render={(props) => <HomePage layout="compact" />} path="..." />
. Cách này hữu dụng khi cần cấu hình nâng cao cho component.
Dùng nhánh con, với cách này component bên trong luôn được React render, nhưng nếu route không so khớp thì nó không được gắn vào DOM để hiển thị lên giao diện.
<Route path="..."> <Homepage layout="full-fledged" /> </Route>
Quy tắc so khớp
Với prop exact
, route phải khớp đến từng ký tự mới với URL path thì mới được tính. Prop này phổ biến trong đa số trường hợp.
Route path | location.pathname | exact | khớp? |
---|---|---|---|
/one/two |
/one/two |
true |
có |
/one |
/one/two |
true |
không |
/one |
/one/two |
false |
có |
Với prop sensitive
, route phải khớp có phân biệt chữ hoa chữ thường (case-sensitive). Prop này ít được dùng trong thực tế.
Route path | location.pathname | sensitive | khớp? |
---|---|---|---|
/one |
/one |
true |
có |
/One |
/one |
true |
không |
/One |
/one |
false |
có |
Component <Switch>
Vì quá quen thuộc với server-side routing nên nhiều người vẫn tâm niệm rằng routing nghĩa là “chuyển trang”, thật ra với client-side routing, một phần tác dụng đúng là đem lại cảm giác “chuyển trang” cho người dùng, nhưng bản chất của nó là hiển thị component có điều kiện.
Khi bọc những định nghĩa <Route> bên trong component <Switch>, chỉ có duy nhất một <Route> được hiển thị nếu nó so khớp, những <Route> còn lại sẽ bị bỏ qua (xét theo thứ tự từ trên xuống). Trong code ví dụ, chúng ta đã lợi dụng đặc tính này của <Switch> để hiển thị thông báo “ROUTE NOT FOUND” khi không có route nào ở trên được so khớp.
Chúng ta có thể có những <Route> nằm ngoài <Switch>, tất cả những <Route> này đều sẽ được xử lý và sẽ có nhiều hơn một <Route> được hiển thị miễn là chúng so khớp.
<Switch> {/* Cả 2 route đều khớp với path "/tech/articles", nhưng chỉ có route ở trên được hiển thị */} <Router path="/tech" /> <Router path="/tech/articles" /> </Switch> {/* Cả 2 route đều khớp với path "/course/mern" và đều được hiển thị */} <Router path="/course" /> <Router path="/course/mern" />
Component <Link> và <NavLink>
Cả hai component này đều hiển thị thẻ <a> và bắt sự kiện onClick, ngăn không cho trình duyệt tự ý tải trang web mới. Component <NavLink> về cơ bản giống với <Link>, chỉ thêm một chức năng là so sánh URL của trình duyệt với giá trị href của thẻ <a>, nếu chúng khớp với nhau thì sẽ thêm một CSS class vào thẻ này. Mặc định class tên “active”, nhưng chúng ta có thể đổi tên khác qua prop activeClassName
.
Tag:react
Facebook Comments