React
React 路由

⚠️ Generate by copilot, need to review and modify

前端路由是什么, 解决的问题?

前端路由, 也被称为客户端路由, 是一种在单页应用(Single Page Application, 简称 SPA)中模拟传统多页面应用行为的技术。在单页应用中, 所有的视图逻辑都在一个 HTML 页面中处理, 通过 JavaScript 动态地改变页面内容, 而不是通过从服务器加载新页面。

前端路由的主要目标是在不进行完整页面刷新的情况下, 改变浏览器的 URL 并呈现与该 URL 相关联的内容。它通过监听 URL 的变化(包括 hash 变化或 HTML5 History API 的变化)来实现, 然后根据新的 URL 渲染对应的组件或者执行对应的逻辑。

前端路由解决的问题:

  1. 用户体验: 前端路由可以在不进行全页面刷新的情况下改变页面内容, 使得用户体验更加流畅和连贯。

  2. 性能优化: 由于只需要加载必要的数据和视图, 而不是整个页面, 因此可以减少不必要的网络请求和渲染, 提高应用的性能。

  3. 搜索引擎优化(SEO): 虽然单页应用因为其动态渲染的特性在 SEO 上有一些挑战, 但是通过正确地使用前端路由(例如, 使用服务器端渲染或预渲染技术), 可以使单页应用更好地被搜索引擎索引。

前端路由的一个常见例子是 React Router, 这是一个用于构建在 React 中使用的路由的库。它允许你定义多个路由, 每个路由都与一个或多个组件关联, 当 URL 匹配到某个路由时, 就会渲染与之关联的组件。

React-router
  • 路由切换方式:

    • <Link> 组件通过 React Router 库来管理路由切换,它会使用 JavaScript 来处理导航事件,而不会触发浏览器的完整页面刷新。相比之下,<a> 标签是传统的 HTML 元素,点击时会触发浏览器的完整页面刷新。
  • 阻止默认行为:

    • 使用 <Link> 组件时,React Router 会拦截点击事件,并使用浏览器的 History API 来切换路由,而不会导致页面刷新。这样可以避免页面的重新加载,提供更流畅的用户体验。相反,<a> 标签的点击事件会触发浏览器的默认行为,即刷新页面或导航到链接的 URL。
  • 路由匹配:

    • <Link> 组件会根据指定的路径和路由配置进行匹配,并生成正确的 URL。它会自动处理基于路由配置的路径匹配逻辑,确保生成的链接与当前路由匹配。相比之下,<a> 标签需要手动设置正确的 URL,没有内置的路由匹配功能。
前端路由如何切换页面

前端路由实现的本质是监听 url 变化, 无需刷新页面就能重新加载相应的页面。

  • Hash url 的格式为www.a.com/#/, 当#后的哈希值发生变化时, 通过 hashchange 事件监听, 然后页面跳转。
  • History url 通过 history.pushState 和 history.replaceState 改变 url。

前端路由切换页面的过程实际上是在单个 HTML 页面内部, 通过 JavaScript 动态地改变页面内容, 而不是从服务器加载新页面。具体来说, 当 URL 改变时, 前端路由会匹配到一个或多个与新 URL 对应的路由规则, 然后根据这些规则来决定渲染哪些组件或执行哪些逻辑。

以下是一个基本的步骤概述:

  1. 监听 URL 变化: 前端路由首先需要监听浏览器的 URL 变化。这可以通过两种方式实现: 一种是监听 hash(#)的变化, 另一种是使用 HTML5 的 History API(pushStatepopState)。

  2. 匹配路由规则: 当 URL 变化时, 前端路由会从预定义的路由规则中找出与新 URL 匹配的规则。路由规则通常是以某种模式(例如正则表达式)定义的, 可以匹配一类 URL。

  3. 渲染组件: 找到匹配的路由规则后, 前端路由会渲染与之关联的组件, 或执行与之关联的逻辑。这通常涉及到更新页面的一部分内容, 而不是整个页面。

  4. 更新浏览器的历史记录: 在 HTML5 的 History API 中, 可以使用 pushState 方法来添加一个新的历史记录, 或使用 replaceState 方法来替换当前的历史记录。这样, 用户就可以使用浏览器的前进和后退按钮来导航应用。

以 React Router 为例, 你可以定义一个路由如下:

<Route path="/about" component={About} />

当 URL 变为 "/about" 时, React Router 会渲染 About 组件。如果你在应用中点击一个链接 <Link to="/about">About</Link>, React Router 会改变 URL 并渲染 About 组件, 而页面并不会刷新。

React Router v6 原理

React Router v6 是一个用于 React 应用的前端路由库。它的工作原理基于以下几个关键概念:

  1. 路由(Routes): 在 React Router v6 中, 路由是通过 <Route> 组件来定义的, 每个 <Route> 组件都有一个 path 属性, 当 URL 与 path 匹配时, 会渲染对应的组件。

  2. 路由匹配(Route Matching): React Router v6 使用 path-to-regexp 库来将路由路径(例如 "/users/:id")转换为正则表达式, 然后用这个正则表达式去匹配当前的 URL。如果匹配成功, React Router v6 会提取出路径参数(例如 id)并传递给对应的组件。

  3. 历史(History): React Router v6 使用 history 库来管理浏览器的历史记录。当你使用 <Link> 组件或 navigate 函数来改变 URL 时, React Router v6 会调用 history.pushhistory.replace 方法来更新浏览器的历史记录。

  4. 上下文(Context): React Router v6 使用 React 的 Context API 来存储路由的状态, 包括当前的 URL、历史记录等。当路由状态改变时, 依赖于这些状态的组件会被重新渲染。

以下是 React Router v6 的一个基本使用示例:

import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
 
function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link> | <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}
 
function Home() {
  return <h1>Home Page</h1>;
}
 
function About() {
  return <h1>About Page</h1>;
}

在这个例子中, 当你点击 "Home" 链接时, URL 会变为 "/", 然后 <Home /> 组件会被渲染;当你点击 "About" 链接时, URL 会变为 "/about", 然后 <About /> 组件会被渲染。这个过程都是在客户端完成的, 没有向服务器发送任何请求。

React Router history 和 hash 路由差别 ?

两种模式的区别:

  1. hash 只能改变#后的值, 而 history 模式可以随意设置同源 url;
  2. hash 只能添加字符串类的数据, 而 history 可以通过 API 添加多种类型的数据;
  3. hash 的历史记录只显示之前的www.a.com而不会显示hash值, 而 history 的每条记录都会进入到历史记录;
  4. hash 无需后端配置且兼容性好, 而 history 需要配置 index.html 用于匹配不到资源的情况
  • history API
    • 通过 history.pushState() 跳转路由
    • 通过 popstate event 监听路由变化, 但无法监听到 history.pushState() 时的路由变化
  • hash
    • 通过 location.hash 跳转路由
    • 通过 hashchange event 监听路由变化
路由持久化 权限管理

路由持久化和权限管理是前端应用中的两个重要问题。以下是如何在 React Router 中处理这两个问题的一些基本策略:

路由持久化:

路由持久化通常涉及到在用户刷新页面或重新打开应用时, 恢复他们之前的路由状态。这可以通过保存当前的 URL(包括路径、查询参数和哈希)到浏览器的本地存储(例如 localStorage), 然后在应用启动时, 检查本地存储并导航到保存的 URL。

这种方法的一个缺点是, 如果你的应用的路由结构发生了变化, 保存的 URL 可能无法正确地恢复用户的路由状态。因此, 你可能需要在保存和恢复 URL 时, 加入一些额外的逻辑来处理这种情况。

权限管理:

权限管理通常涉及到根据用户的权限, 决定他们可以访问哪些路由。这可以通过在 <Route> 组件中添加一个权限检查的逻辑来实现。

例如, 你可以创建一个 ProtectedRoute 组件, 它会先检查用户是否有权限访问该路由, 如果有权限, 就渲染对应的组件;如果没有权限, 就重定向到一个错误页面或登录页面。

以下是一个基本的 ProtectedRoute 组件的示例:

import { Route, Navigate } from "react-router-dom";
 
function ProtectedRoute({ component: Component, ...rest }) {
  const isAuthenticated = checkUserAuthentication(); // replace this with your authentication check logic
 
  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? <Component {...props} /> : <Navigate to="/login" />
      }
    />
  );
}

在这个例子中, checkUserAuthentication 是一个函数, 它会检查用户是否已经登录或是否有权限访问当前的路由。如果用户已经登录, 就渲染对应的组件;如果用户没有登录, 就重定向到登录页面。

注意: 这种方法只能防止未经授权的用户在前端访问受保护的路由。为了确保安全, 你还需要在后端进行权限检查, 防止未经授权的用户直接访问受保护的数据。

React 的异步组件是怎么加载的

React 的异步组件是通过使用 React.lazy()Suspense 组件来实现的

import React, { Suspense } from "react";
 
const MyComponent = React.lazy(() => import("./MyComponent"));
 
function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <MyComponent />
      </Suspense>
    </div>
  );
}
 
export default App;