Hướng dẫn đầy đủ về React Router: Mọi thứ bạn cần biết

Có lẽ không có gì ngạc nhiên với bạn khi React Router là thư viện bên thứ 3 phổ biến nhất trong hệ sinh thái React. Trên thực tế, React Router đã được đưa vào 44% tổng số các dự án React. Chỉ riêng số liệu thống kê này là đủ để tuyên bố React-router là kiến ​​thức cần thiết cho bất kỳ nhà phát triển React nào

Để làm cho nó dễ nuốt hơn một chút, bài đăng này là đỉnh cao của mọi thứ bạn cần biết để sử dụng React Router hiệu quả.

Trong hướng dẫn React Router này, chúng ta sẽ bắt đầu với một cái nhìn cấp cao về React Router là gì. Từ đó, chúng ta sẽ đi sâu vào các nguyên tắc cơ bản của API. Cuối cùng, chúng ta sẽ kết thúc việc xem xét một số trường hợp sử dụng khác nhau mà bạn có thể gặp phải trong thế giới thực.

React Router là gì?

Được tạo lần đầu tiên vào năm 2014, React Router là một thư viện định tuyến dựa trên thành phần, máy khách và phía máy chủ khai báo cho React. Cũng giống như React cung cấp cho bạn một API có thể khai báo và tổng hợp để thêm vào và cập nhật trạng thái ứng dụng, React Router cung cấp cho bạn một API có thể khai báo và tổng hợp để thêm vào và cập nhật lịch sử điều hướng của người dùng.

Nếu bạn là người mới sử dụng React, có thể sẽ ngạc nhiên khi biết rằng một bộ định tuyến không được đưa vào thư viện, nhưng đó là nền tảng cho các đặc tính của React. React tập trung vào việc cung cấp cho bạn giao diện người dùng ban đầu để xây dựng ứng dụng của bạn và không có gì hơn.

Về mặt thi pháp, React Router tuân theo một đặc tính tương tự, ngoại trừ thay vì các nguyên tắc giao diện người dùng, chúng cung cấp cho bạn các nguyên tắc định tuyến. Để phù hợp với React, theo lẽ tự nhiên, những “nguyên bản định tuyến” này thực sự chỉ là một tập hợp các thành phần React và Hooks.

Hãy đi sâu vào những cái quan trọng nhất trước khi xem xét các trường hợp sử dụng cụ thể.

BrowserRouter

Đương nhiên, để làm được điều đó, React Router cần phải biết và kiểm soát vị trí ứng dụng của bạn. Cách nó thực hiện điều này là với thành phần BrowserRouter của nó.

Về cơ bản, BrowserRouter sử dụng cả thư viện lịch sử cũng như React Context. Thư viện lịch sử giúp React Router theo dõi lịch sử duyệt của ứng dụng bằng cách sử dụng ngăn xếp lịch sử có sẵn của trình duyệt và React Context giúp cung cấp lịch sử ở bất cứ nơi nào React Router cần.

Không có nhiều thứ đối với BrowserRouter, bạn chỉ cần đảm bảo rằng nếu đang sử dụng React Router trên web, bạn sẽ bọc ứng dụng của mình bên trong thành phần BrowserRouter.

import ReactDOM from 'react-dom'
import * as React from 'react'
import { BrowserRouter } from 'react-router-dom'
import App from './App`

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
, document.getElementById('app'))

<Routers /> khác
Nếu bạn đang sử dụng React Router trong một môi trường không phải là trình duyệt, hãy xem MemoryRouter và StaticRouter.
MemoryRouter theo dõi lịch sử của ứng dụng trong bộ nhớ, thay vì trong URL. Sử dụng cái này thay vì BrowserRouter nếu bạn đang phát triển một ứng dụng React Native.
StaticRouter, như tên của nó, rất hữu ích trong các môi trường mà vị trí của ứng dụng không bao giờ thực sự thay đổi, như khi hiển thị một tuyến duy nhất tới HTML tĩnh trên máy chủ.

Bây giờ bạn đã biết cách kích hoạt React Router thông qua thành phần BrowserRouter, hãy xem cách bạn thực sự có thể yêu cầu React Router tạo một tuyến đường mới.

Route

Nói một cách đơn giản, Route cho phép bạn ánh xạ vị trí ứng dụng của mình với các thành phần React khác nhau. Ví dụ: giả sử chúng tôi muốn hiển thị thành phần Dashboard bất cứ khi nào người dùng điều hướng đến đường dẫn /Dashboard . Để làm như vậy, chúng tôi sẽ hiển thị một Route trông như thế này.

<Route path="/dashboard" element={<Dashboard />} />

Mô hình tinh thần mà tôi sử dụng cho Route là nó luôn phải hiển thị một cái gì đó - hoặc element của nó hỗ trợ nếu path khớp với vị trí hiện tại của ứng dụng hoặc null, nếu nó không.

Bạn có thể hiển thị bao nhiêu Tuyến tùy thích.

<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/settings" element={<Settings />} />

Bạn thậm chí có thể hiển thị các tuyến đường lồng nhau mà chúng ta sẽ nói sau trong bài đăng này.

Với các phần tử Route của chúng tôi trong cấu hình này, nhiều tuyến đường có thể khớp trên một URL duy nhất. Đôi khi bạn có thể muốn làm điều đó, nhưng thường thì bạn muốn React Router chỉ hiển thị tuyến đường phù hợp nhất. May mắn thay, chúng ta có thể dễ dàng làm điều đó với Routes.

Routes

Bạn có thể nghĩ về Routes như là người dẫn đường ẩn dụ cho các tuyến đường của bạn. Bất cứ khi nào bạn có một hoặc nhiều Routes, rất có thể bạn sẽ muốn gói chúng trong một Routes.

import { Routes, Route } from "react-router-dom";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/settings" element={<Settings />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

Lý do cho điều này là vì công việc của Routes là hiểu tất cả các phần tử con của nó, và chọn một cách thông minh những phần tử nào là tốt nhất để hiển thị.

Mặc dù nó không được hiển thị trong ví dụ đơn giản ở trên, nhưng khi chúng tôi bắt đầu thêm các Tuyến phức tạp hơn vào ứng dụng của mình, các Tuyến sẽ bắt đầu thực hiện nhiều công việc hơn như cho phép hiển thị thông minh và các đường dẫn tương đối. Chúng ta sẽ xem những tình huống này chốc lát.

Tiếp theo, liên kết giữa các trang.

Link

Bây giờ bạn đã biết cách ánh xạ vị trí của ứng dụng với các thành phần React nhất định bằng cách sử dụng RoutesRoute, bước tiếp theo là có thể điều hướng giữa chúng. Đây là mục đích của thành phần Link.

Để cho Link biết đường dẫn nào sẽ đưa người dùng đến khi được nhấp vào, bạn chuyển nó to để hỗ trợ.

<nav>
  <Link to="/">Home</Link>
  <Link to="/about">About</Link>
  <Link to="/settings">Settings</Link>
</nav>

Nếu bạn cần kiểm soát nhiều hơn đối với Liên kết, bạn cũng có thể to dưới dạng một đối tượng. Làm như vậy cho phép bạn thêm một chuỗi truy vấn thông qua thuộc tính search hoặc chuyển bất kỳ dữ liệu nào đến tuyến đường mới thông qua state.

<nav>
  <Link to="/">Home</Link>
  <Link to="/about">About</Link>
  <Link
    to={{
      pathname: "/settings",
      search: "?sort=date",
      state: { fromHome: true },
    }}
  >
    Settings
  </Link>
</nav>

Chúng ta sẽ trình bày sâu hơn về state, Chuỗi truy vấn và cách React Router hỗ trợ các đường dẫn tương đối trong phần sau của bài đăng này.

Tại thời điểm này, chúng tôi đã đề cập đến cả lịch sử và các nguyên tắc cơ bản tuyệt đối của React Router, nhưng có một điều đã rõ ràng - bằng cách bao hàm thành phần, React Router thực sự là một bộ định tuyến cho React. Tôi tin rằng React sẽ giúp bạn trở thành một nhà phát triển JavaScript tốt hơn và React Router sẽ khiến bạn trở thành một nhà phát triển React tốt hơn.

Bây giờ, thay vì chỉ hướng dẫn bạn qua phần còn lại của API, chúng tôi sẽ thực hiện một cách tiếp cận thực tế hơn bằng cách chia nhỏ tất cả các trường hợp sử dụng phổ biến mà bạn sẽ cần khi sử dụng React Router.

URL Parameters

Giống như các tham số hàm cho phép bạn khai báo trình giữ chỗ khi bạn xác định một hàm, Tham số URL cho phép bạn khai báo trình giữ chỗ cho các phần của URL.

Lấy Wikipedia làm ví dụ. Khi bạn truy cập một chủ đề trên Wikipedia, bạn sẽ nhận thấy rằng mẫu URL luôn giống nhau, wikipedia.com/wiki/{topicId}.

Thay vì xác định lộ trình cho mọi chủ đề trên trang web, họ có thể khai báo một lộ trình với trình giữ chỗ cho id của chủ đề. Cách bạn nói với React Router rằng một phần nhất định của URL là trình giữ chỗ (hoặc Tham số URL), là bằng cách sử dụng: trong phần hỗ trợ đường dẫn của Route

<Route path="/wiki/:topicId" element={<Article />} />

Giờ đây, bất cứ khi nào bất cứ ai truy cập vào một URL khớp với mẫu / wiki /: topicId (/ wiki / javascript, / wiki / Brendan_Eich, / wiki / anything), thành phần Article sẽ được hiển thị.

Bây giờ câu hỏi là, làm cách nào để bạn truy cập phần động của URL - trong trường hợp này là topicId - trong thành phần được hiển thị?

Kể từ v5.1, React Router đi kèm với useParams Hook trả về một đối tượng có ánh xạ giữa (các) tham số URL và giá trị của nó.

import * as React from 'react'
import { useParams } from 'react-router-dom'
import { getArticle } from '../utils'

function Article () {
  const [article, setArticle] = React.useState(null)
  const { topicId } = useParams()

  React.useEffect(() => {
    getArticle(topicId)
      .then(setUser)
  }, [topicId])

  return (
    ...
  )
}

Nested Routes

Các tuyến lồng nhau cho phép Tuyến mẹ hoạt động như một trình bao bọc và kiểm soát việc hiển thị một Tuyến con.

Một ví dụ thực tế về giao diện người dùng này có thể trông tương tự như tuyến đường messages / của Twitter. Khi bạn truy cập / messages, bạn sẽ thấy tất cả các cuộc Chat trước đó của mình ở bên trái màn hình. Sau đó, khi bạn truy cập / messages /: id, bạn vẫn thấy tất cả các messages của mình, nhưng bạn cũng thấy lịch sử Chat của mình cho: id.

Hãy xem cách chúng ta có thể triển khai kiểu định tuyến lồng nhau này với React Router. Chúng ta sẽ bắt đầu với một số Lộ trình cơ bản.

// App.js
function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages" element={<Messages />} />
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

Bây giờ, nếu chúng ta muốn Messages kiểm soát việc hiển thị các Tuyến con, thì điều gì ngăn chúng ta chỉ hiển thị một thành phần Định tuyến khác bên trong Messages? Một cái gì đó như thế này:

function Messages() {
  return (
    <Container>
      <Conversations />

      <Routes>
        <Route path=":id" element={<Chat />} />
      </Routes>
    </Container>
  );
}

Bây giờ khi người dùng điều hướng đến / messages, React Router sẽ hiển thị thành phần Messages. Từ đó, Messages hiển thị tất cả các cuộc Chat của chúng tôi thông qua thành phần Chat và sau đó hiển thị các Routes khác có Route ánh xạ / Messages /: id tới thành phần Chat.

Các tuyến đường tương đối
Lưu ý rằng chúng ta không phải bao gồm đường dẫn đầy đủ / messages /: id trong Route lồng nhau. Điều này là do Routes là thông minh và bằng cách bỏ đi đầu /, nó giả định rằng chúng ta muốn đường dẫn này có liên quan đến vị trí của cha , /messages

Có vẻ tốt, nhưng có một vấn đề nhỏ. Bạn có thể phát hiện ra nó không?

messages chỉ được hiển thị khi người dùng đang ở / messages. Khi họ truy cập vào một URL khớp với mẫu / messages /: id, messages không còn khớp nữa và do đó, các Tuyến lồng nhau của chúng tôi sẽ không bao giờ được hiển thị.

Để khắc phục điều này, theo lẽ tự nhiên, chúng ta cần một cách để cho React Router biết rằng chúng ta muốn hiển thị Messages khi người dùng đang ở / messages hoặc bất kỳ vị trí nào khác khớp với mẫu / messages / *.

Chờ đợi. Điều gì sẽ xảy ra nếu chúng ta chỉ cập nhật đường dẫn của mình thành / messages / *?

// App.js
function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages/*" element={<Messages />} />
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

Chúng tôi rất vui, điều đó sẽ hoạt động. Bằng cách thêm / * vào cuối đường dẫn / messages, về cơ bản chúng ta đang nói với React Router rằng Messages có thành phần Routes lồng nhau và đường dẫn mẹ của chúng ta phải khớp với / messages cũng như bất kỳ vị trí nào khác khớp với / messages / * họa tiết. Chính xác những gì chúng tôi muốn.

Tại thời điểm này, chúng tôi đã xem xét cách bạn có thể tạo các tuyến đường lồng nhau bằng cách thêm / * vào đường dẫn và kết xuất của Tuyến đường của chúng ta, theo nghĩa đen, một thành phần Các tuyến đường lồng nhau. Điều này hoạt động khi bạn muốn Tuyến đường con của bạn kiểm soát việc hiển thị các Tuyến đường lồng nhau, nhưng điều gì sẽ xảy ra nếu chúng ta muốn thành phần Ứng dụng của mình chứa tất cả thông tin cần thiết để tạo các tuyến đường lồng nhau của chúng ta thay vì phải làm điều đó bên trong Tin nhắn?

Bởi vì đây là một sở thích phổ biến, React Router cũng hỗ trợ cách này để tạo các tuyến đường lồng nhau. Đây là những gì nó trông như thế nào.

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages" element={<Messages />}>
        <Route path=":id" element={<Chats />} />
      </Route>
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

Bạn đã khai báo lồng Route con dưới dạng con của Route cha. Giống như trước đây, Route con bây giờ là tương đối với cha, vì vậy bạn không cần phải bao gồm đường dẫn cha (/ messages).

Bây giờ, điều cuối cùng bạn cần làm là cho React Router biết vị trí trong Tuyến cha (massages) sẽ hiển thị Tuyến con (Chats).

Để làm điều này, bạn sử dụng thành phần Outlet của React Router

import { Outlet } from "react-router-dom";

function Messages() {
  return (
    <Container>
      <Conversations />

      <Outlet />
    </Container>
  );
}

Nếu vị trí của ứng dụng khớp với đường dẫn của Tuyến đường lồng nhau, thành phần Outlet này sẽ hiển thị phần tử của Tuyến đường. Vì vậy, dựa trên Routes của chúng ta ở trên, nếu chúng ta đang ở / messages, thành phần Outlet sẽ hiển thị null, nhưng nếu chúng ta ở / messages / 1, nó sẽ hiển thị thành phần <Chats />.

Pass props to Router Components

Trong các phiên bản trước của React Router (v4), điều này không hề nhỏ vì React Router chịu trách nhiệm tạo phần tử React.

Tuy nhiên, với React Router v6, vì bạn chịu trách nhiệm tạo phần tử, nên bạn chỉ cần chuyển một phần tử hỗ trợ cho thành phần như bình thường.

<Route path="/dashboard" element={<Dashboard authed={true} />} />

Programmatically Navigate

React Router cung cấp hai cách khác nhau để điều hướng theo chương trình, tùy thuộc vào sở thích của bạn. Đầu tiên là phương thức điều hướng bắt buộc và thứ hai là thành phần Điều hướng khai báo.

Để có quyền truy cập vào phương thức điều hướng bắt buộc, bạn sẽ cần sử dụng useNavigate Hook của React Router. Từ đó, bạn có thể chuyển điều hướng đến đường dẫn mới mà bạn muốn người dùng được đưa đến khi điều hướng được gọi.

import { useNavigate } from 'react-router-dom

function Register () {
  const navigate = useNavigate()

  return (
    <div>
      <h1>Register</h1>
      <Form afterSubmit={() => navigate('/dashboard')} />
    </div>
  )
}

Nếu bạn thích một cách tiếp cận khai báo hơn, bạn có thể sử dụng thành phần Điều hướng của Bộ định tuyến React.

Điều hướng hoạt động giống như bất kỳ thành phần React nào khác, tuy nhiên, thay vì hiển thị một số giao diện người dùng, nó điều hướng người dùng đến một vị trí mới.

function Register() {
  const [toDashboard, setToDashboard] = React.useState(false);

  if (toDashboard === true) {
    return <Navigate to="/dashboard" />;
  }

  return (
    <div>
      <h1>Register</h1>
      <Form afterSubmit={() => toDashboard(true)} />
    </div>
  );
}

Nó phải nhập nhiều hơn, nhưng tôi tranh luận rằng trạng thái rõ ràng dẫn đến một API khai báo tốt hơn trạng thái ngầm được xử lý bởi một API bắt buộc.

Query Strings

Bạn gần như chắc chắn đã gặp phải các chuỗi truy vấn trước đây. Họ là?& bạn thấy được nối vào các URL. Chúng là một khía cạnh cơ bản của cách hoạt động của Web vì chúng cho phép bạn chuyển trạng thái qua URL.

Vi du: Query String
twitter.com/search?q=ui.dev&src=typed_query&f=live

Trên đây là ví dụ về chuỗi truy vấn bạn sẽ thấy nếu tìm kiếm ui.dev trên Twitter.

Kể từ phiên bản 6, React Router chủ yếu dựa vào API URLSearchParams để xử lý các chuỗi truy vấn. URLSearchParams được tích hợp vào tất cả các trình duyệt (ngoại trừ IE) và cung cấp cho bạn các phương thức tiện ích để xử lý các chuỗi truy vấn. Để thực hiện điều này, React Router đi kèm với một hookSearchParams sử dụng tùy chỉnh, là một trình bao bọc nhỏ trên URLSearchParams.

useSearchParams trả về một mảng với phần tử đầu tiên là một phiên bản của URLSearchParams và phần tử thứ hai là một cách để cập nhật chuỗi truy vấn.

Sử dụng URL Twitter mà chúng tôi đã thấy ở trên, đây là cách chúng tôi lấy các giá trị từ chuỗi truy vấn của mình bằng cách sử dụng useSearchParams.

import { useSearchParams } from 'react-router-dom'

const Results = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  const q = searchParams.get('q')
  const src = searchParams.get('src')
  const f = searchParams.get('f')

  return (
    ...
  )
}

Catch all (404) Pages

Tất cả những gì bạn phải làm là kết xuất một Lộ trình với đường dẫn là * và React Router sẽ đảm bảo chỉ hiển thị phần tử nếu không có Đường dẫn nào khác khớp

<Routes>
  <Route path="*" element={<NotFound />} />
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/settings" element={<Settings />} />
</Routes>

Không giống như các phiên bản trước của Bộ định tuyến React, thứ tự của các tuyến con không quan trọng vì các tuyến rất thông minh - có nghĩa là một thuật toán hiện xác định đâu là tuyến tốt nhất để hiển thị. Điều này làm cho việc hiển thị một thành phần 404 khá đơn giản.

Pass props to Link

Để chuyển dữ liệu qua một thành phần Liên kết đến một tuyến đường mới, hãy sử dụng bộ phận hỗ trợ trạng thái của Liên kết

<Link to="/onboarding/profile" state={{ from: "occupation " }}>
  Next Step
</Link>

Bất cứ khi nào bạn chuyển dữ liệu qua phần hỗ trợ trạng thái, dữ liệu đó sẽ có sẵn trên thuộc tính trạng thái của vị trí, bạn có thể truy cập vào thuộc tính này bằng cách sử dụng useLocation Hook tùy chỉnh đi kèm với React Router.

import { useLocation } from 'react-router-dom'

function Profile () {
  const location = useLocation()
  const { from } = location.state

  return (
    ...
  )
}

Rendering a Sidebar

Việc hiển thị một thanh bên với React Router không đặc biệt thú vị vì nó chỉ là một tập hợp các Liên kết. Tuy nhiên, điều gì sẽ xảy ra nếu chúng ta muốn thanh bên đó cũng nhận biết được vị trí của ứng dụng? Chắc chắn bạn có thể sử dụng useLocation Hook của React Router cho việc này, nhưng React Router đi kèm với một công cụ tốt hơn để ánh xạ vị trí của ứng dụng với các thành phần nhất định, cụ thể là Routes và Route.

Chìa khóa để hiển thị thanh bên nhận biết vị trí là hiểu rằng với Bộ định tuyến React, bạn có thể hiển thị bao nhiêu Tuyến tùy thích. Có thể bạn đã quen với việc hiển thị các Tuyến đường ở cấp cao nhất của ứng dụng, nhưng không có gì ngăn cản bạn hiển thị các Tuyến đường khác ở một nơi khác trong ứng dụng của bạn, chẳng hạn như trong thanh bên.

export default function App() {
  return (
    <div className="wrapper">
      <div className="sidebar">
        <ul className="nav">
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/p">Profile</Link>
          </li>
          <li>
            <Link to="/s">Settings</Link>
          </li>
        </ul>

        <Routes>
          <Route path="/" element={<HomeDesc />} />
          <Route path="/p" element={<ProfileDesc />} />
          <Route path="/s" element={<SettingsDesc />} />
        </Routes>
      </div>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/p" element={<Profile />} />
        <Route path="/s" element={<Settings />} />
      </Routes>
    </div>
  );
}

Customizing Link

Một điều tôi thích ở React Router là cachs nó có thể kết hợp. Khái niệm này thực sự tỏa sáng khi bạn cần xây dựng thành phần Liên kết tùy chỉnh của riêng mình. Vì React Router có API đầu tiên của thành phần, bạn có thể soạn Liên kết giống như bạn soạn bất kỳ thành phần React nào khác.

Giả sử chúng tôi muốn tạo một thành phần Liên kết tùy chỉnh “phát sáng” và thêm biểu tượng cảm xúc 👉 vào bất kỳ Liên kết nào đang hoạt động. Để làm điều đó, tất cả những gì chúng ta phải làm là soạn Liên kết và sau đó useLocation để lấy vị trí hiện tại của ứng dụng.

import { useLocation } from 'react-router-dom'

function GlowLink ({ children, to }) {
  const location = useLocation()
  const match = location.pathname === to

  return (
    <span className={match ? 'glow' : ''}>
      {match ? '👉 ' : ''}
      <Link to={to}>
        {children}
      </Link>
    </span>
  )
}

...


<nav>
  <GlowLink to='/'>Home</GlowLink>
  <GlowLink to='/about'>About</GlowLink>
  <GlowLink to='/features'>Features</GlowLink>
</nav>

Animated Transitions

Thật không may, nếu bạn đang sử dụng React Router v6, hiện tại không có câu chuyện tuyệt vời để thêm chuyển tiếp động vào ứng dụng của bạn. Điều này là do React Router đã loại bỏ thành phần Switch, đây là một phần cơ bản trong cách bạn hoàn thành nó với các phiên bản trước.

Khi vấn đề này được giải quyết, chúng tôi sẽ cập nhật bài đăng này.

Nếu bạn đang sử dụng phiên bản React Router khác, hãy xem một trong các bài viết sau.

Code Splitting

Nếu có một định kiến ​​về các nhà phát triển JavaScript thường đúng hơn mức cần thiết, thì đó là việc thiếu quan tâm đến kích thước gói lớn. Về mặt lịch sử, vấn đề là quá dễ dàng để làm đầy gói JavaScript của bạn và quá khó để làm bất cứ điều gì về nó. Đây là nơi mà tính năng Tách mã có thể giúp ích.

Ý tưởng rất đơn giản, đừng tải mã xuống cho đến khi người dùng cần. Người dùng của bạn không cần phải tải xuống toàn bộ ứng dụng của bạn khi tất cả những gì họ cần là một phần của nó. Nếu người dùng đang tạo một bài đăng mới, việc họ tải xuống tất cả mã cho tuyến đường / đăng ký là không hợp lý. Nếu người dùng đang đăng ký, họ không cần trình soạn thảo văn bản đa dạng thức khổng lồ mà ứng dụng của bạn cần trên lộ trình / settings. Điều đó thật lãng phí và một số người sẽ cho rằng không tôn trọng những người dùng không có đặc quyền về băng thông không giới hạn. Code Splitting không chỉ trở nên phổ biến hơn trong những năm gần đây mà còn trở nên dễ dàng hơn theo cấp số nhân.

Đây là cách nó hoạt động. Thay vì coi nhập như một từ khóa như bạn thường làm, bạn sử dụng nó giống như một hàm trả về Lời hứa. Lời hứa này sẽ giải quyết với mô-đun sau khi mô-đun được tải hoàn toàn.

if (editingPost === true) {
  import('./editpost')
    .then((module) => module.showEditor())
    .catch((e) => )
}

Bây giờ có một phần nữa cho câu đố tách mã mà chúng ta cần xem xét và đó là React.lazy.

React.lazy nhận vào một đối số duy nhất, một hàm gọi nhập động và trả về một Thành phần React thông thường.

const LazyHomeComponent = React.lazy(
  () => import('./Home')
)

...

<LazyHomeComponent />

Điều đặc biệt về LazyHomeComponent là React sẽ không tải nó cho đến khi nó cần, khi nó được kết xuất. Điều đó có nghĩa là, nếu chúng ta kết hợp React.lazy với React Router, chúng ta có thể tạm dừng việc tải bất kỳ thành phần nào cho đến khi người dùng truy cập vào một đường dẫn nhất định.

import * as React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";

import Loading from "./Loading";

const Home = React.lazy(() => import("./Home"));
const Topics = React.lazy(() => import("./Topics"));
const Settings = React.lazy(() => import("./Settings"));

export default function App() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
          <li>
            <Link to="/settings">Settings</Link>
          </li>
        </ul>

        <hr />

        <React.Suspense fallback={<Loading />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/topics" element={<Topics />} />
            <Route path="/settings" element={<Settings />} />
          </Routes>
        </React.Suspense>
      </div>
    </Router>
  );
}

Lưu ý rằng chúng ta cần phải gói gọn các Routes lazing bên trong React.Suspense. Điều thú vị về React.Suspense là Suspense có thể nhận nhiều thành phần, được tải một cách lười biếng trong khi vẫn chỉ hiển thị một phần tử dự phòng.

Bây giờ thay vì tải trước toàn bộ ứng dụng của chúng tôi, React sẽ chỉ tải các thành phần Trang chủ, Chủ đề và Cài đặt của chúng tôi khi chúng cần.

Protected Routes

Thông thường, khi xây dựng một ứng dụng web, bạn sẽ cần phải bảo vệ các tuyến đường nhất định trong ứng dụng của mình khỏi những người dùng không có xác thực thích hợp.

Mặc dù React Router không cung cấp bất kỳ chức năng nào cho điều này, bởi vì nó được xây dựng với tính đến khả năng kết hợp, việc bổ sung nó khá dễ dàng.

Hãy để tôi đề xuất API cuối cùng có thể trông như thế nào, trước khi chúng ta đi sâu vào triển khai. Điều gì sẽ xảy ra nếu, đối với mọi tuyến đường chúng ta muốn ở chế độ riêng tư, thay vì cung cấp cho phần tử Routes của chúng tôi hỗ trợ thành phần mà chúng tôi muốn nó hiển thị trực tiếp, chúng tôi bọc nó bên trong một thành phần mới mà chúng tôi sẽ gọi là RequestAuth.

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/pricing" element={<Pricing />} />
  <Route
    path="/dashboard"
    element={
      <RequireAuth>
        <Dashboard />
      </RequireAuth>
    }
  />
  <Route
    path="/settings"
    element={
      <RequireAuth>
        <Settings />
      </RequireAuth>
    }
  />
  <Route path="/login" element={<Login />} />
</Routes>

Tại thời điểm này, chúng ta biết hai điều chính về RequiAuth. Đầu tiên, nó chỉ có api là một phần tử con. Thứ hai, nếu người dùng được xác thực, nó sẽ hiển thị phần tử con đó, nếu không, nó sẽ chuyển hướng người dùng đến một trang mà họ có thể xác thực (trong trường hợp của chúng tôi là / login).

Giả sử bạn có thể nhận được trạng thái xác thực của người dùng của mình từ móc useAuth tùy chỉnh, thì RequestAuth trở nên khá đơn giản.

function RequireAuth({ children }) {
  const { authed } = useAuth();
  const location = useLocation();

  return authed === true ? (
    children
  ) : (
    <Navigate to="/login" replace state={{ path: location.pathname }} />
  );
}

Lưu ý vì chúng tôi nhận được vị trí ban đầu mà người dùng đang cố gắng truy cập thông qua hook useLocation và chuyển vị trí đó làm điểm tựa trạng thái khi chúng tôi chuyển hướng họ đến / đăng nhập, sau khi họ xác thực, chúng tôi có thể chuyển hướng họ trở lại đường dẫn ban đầu này.

// In the Login component
const handleLogin = () => {
  login().then(() => {
    navigate(state?.path || "/dashboard");
  });
};

Preventing Transitions

Cho đến hôm nay, React Router v6 không được hỗ trợ để ngăn chặn quá trình chuyển đổi. Sau khi vấn đề này được giải quyết, chúng tôi sẽ cập nhật bài đăng này với cách được đề xuất để ngăn chuyển đổi trong ứng dụng của bạn.

Route Config

React Router v6 đi kèm với useRoutes Hook giúp việc sắp xếp các tuyến của bạn thành một cấu hình tuyến trung tâm không chỉ khả thi mà còn đơn giản với API lớp đầu tiên.

Giả sử chúng tôi có các đường dẫn sau trong ứng dụng của mình.

/
/invoices
:id
pending
complete
/users
:id
settings

Thông thường, nếu bạn muốn ánh xạ các đường dẫn đó đến các thành phần React khác nhau, bạn sẽ hiển thị một cái gì đó như thế này.

return (
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/invoices" element={<Invoices />}>
      <Route path=":id" element={<Invoice />} />
      <Route path="pending" element={<Pending />} />
      <Route path="complete" element={<Complete />} />
    </Route>
    <Route path="/users/*" element={<Users />} />
  </Routes>
);

Bây giờ với useRoutes, thay vì khai báo các tuyến đường của bạn bằng cách sử dụng các phần tử React (JSX), bạn có thể thực hiện việc đó bằng cách sử dụng các đối tượng JavaScript.

useRoutes nhận một mảng các đối tượng JavaScript đại diện cho các tuyến đường trong ứng dụng của bạn. Tương tự như API phần tử React với , mỗi tuyến có một đường dẫn, phần tử và thuộc tính con tùy chọn.

import { useRoutes } from "react-router-dom";

const routes = useRoutes([
  { path: "/", element: <Home /> },
  {
    path: "/invoices",
    element: <Invoices />,
    children: [
      { path: ":id", element: <Invoice /> },
      { path: "/pending", element: <Pending /> },
      { path: "/complete", element: <Complete /> },
    ],
  },
  {
    path: "/users",
    element: <Users />,
    children: [
      { path: ":id", element: <Profile /> },
      { path: "/settings", element: <Settings /> },
    ],
  },
]);

export default function App() {
  return (
    <div>
      <Navbar />
      {routes}
    </div>
  );
}

Điều khiến useRoutes trở nên thú vị hơn là cách React Router sử dụng nó trong nội bộ. Trên thực tế, khi bạn sử dụng API phần tử React để tạo các tuyến đường của mình, nó thực sự chỉ là một trình bao bọc xung quanh useRoutes.

Server Rendering

Nếu kết xuất máy chủ là một khái niệm mới đối với bạn, điều quan trọng là phải nắm được bức tranh toàn cảnh về cách tất cả các phần của kết xuất máy chủ khớp với nhau trước khi đi sâu vào chi tiết.

  1. Người dùng nhập URL của bạn vào trình duyệt web của họ và nhấn enter

  2. Máy chủ của bạn thấy có một yêu cầu GET

  3. Máy chủ kết xuất ứng dụng React của bạn thành một chuỗi HTML, bao bọc nó bên trong tài liệu HTML chuẩn (DOCTYPE và tất cả) và gửi lại toàn bộ nội dung dưới dạng phản hồi

pic

Trình duyệt thấy rằng nó có một tài liệu HTML trở lại từ máy chủ và công cụ kết xuất của nó sẽ hoạt động để hiển thị trang

  1. Sau khi hoàn tất, trang có thể xem được và trình duyệt bắt đầu tải xuống bất kỳ