# 前端路由原理

  • 原理:检测浏览器 url 的变化,截获 url 地址,然后进行 url 路由匹配。

  • 有 hash 和 history 两种模式。

  • hash 模式带有 “#” 符号,可以监听 hashchange 事件。

  • history 模式新增了 pushState、replaceState 方法以及 onpopstate 事件。

  • 注意:页面刷新时,hash 模式下浏览器不会向服务器发送请求,但是 history 模式就会。

自己实现一个简单的 hash 模式路由机制:

import { useHash } from "react-use";
import _ from "lodash";
const Page1 = () => "Page 1";
const Page2 = () => "Page 2";
const Page3 = () => "Page 3";
const Page4 = () => "Page 4";

const MyRouter = ({ children }) => {
  const routes = _.keyBy(
    children.map((c) => c.props),
    "path",
  );
  const [hash] = useHash();
  // 通过 “#” 后面的部分来决定具体渲染哪个组件到主区域
  const Page = routes[hash.replace("#", "")]?.component;
  return Page ? <Page /> : "Not found.";
};

const Route = () => null;
export default () => {
  return (
    <>
      <h1>My Router</h1>
      <div className="exp-15-my-router">
        <div className="exp-15-sider">
          <a href="#page1">Page 1</a>
          <a href="#page2">Page 2</a>
          <a href="#page3">Page 3</a>
          <a href="#page4">Page 4</a>
        </div>
        <div className="exp-15-page-container">
          <MyRouter>
            <Route path="page1" component={Page1} />
            <Route path="page2" component={Page2} />
            <Route path="page3" component={Page3} />
            <Route path="page4" component={Page4} />
          </MyRouter>
        </div>
      </div>
    </>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# React Router 使用

React Router (opens new window) 包含以下 3 个包:

  • react-router(路由基础库)

  • react-router-dom(基于浏览器环境的再次封装)

  • react-router-native(基于 react-native 环境的再次封装)

# 安装

如果是在浏览器中,就安装 react-router-dom;如果是在 react native 中,就安装 react-router-native

# 基础路由配置

一个最基本的路由必须要用到 BrowserRouter 和 Route 这两个组件。

  • HashRouter 组件,hash 模式

  • BrowserRouter 组件,history 模式

  • Route 组件

# 常见组件

# 1. Router 组件

  • 每个 router 都会创建一个 history 对象,用来保持当前位置的追踪。

  • web 端

    HashRouter:只处理静态的 url

    BrowserRouter:非静态的站点,要处理不同的 url

  • react native 端

    MemoryHistory

# 2. Route 组件

  • 只是一个具有渲染方法的普通 react 组件,路由匹配成功渲染该组件。

  • 常用属性

    path,路由匹配规则,可以省略,字符串类型。如果没有指定 path,无论访问什么路由,都会匹配到

    exact,布尔类型,设置为 true 的话就是严格模式

    {/* 指定严格模式后就匹配不到 Info 组件了 */}
    <Route path="/" exact component={Info} />
    
    1
    2

    component,要渲染的组件

    render,函数形式,渲染 JSX 代码,可以进行逻辑操作,只有在 path 匹配的时候才执行

    <Route
      path="/render"
      render={() => {
        return <h1>Render</h1>
      }} 
    />
    
    1
    2
    3
    4
    5
    6

    children,函数形式,也可以做一些逻辑操作,任何时候都会执行。有一个 match 对象,匹配到路由时对象就有值,匹配不到路由时,就为 null。

    {/* 当访问的路由是 /children 时,页面显示 Children/children,否则显示 Childrennull */}
    <Route
      path="/children"
      children={({match}) => {
        return <h1>Children{ match ? match.path : match + '' }</h1>
      }}
    />
    
    1
    2
    3
    4
    5
    6
    7

    优先级:children > component > render

# 3. Switch 组件

  • 最多只会匹配一个组件。如果第一个组件匹配到了,就不会继续往下匹配了。

    <Switch>
      {/* 指定严格模式后就匹配不到 Info 组件了 */}
      {/* 如果没有指定严格模式,那么 Switch 组件就会匹配到 Info 组件,从而渲染 Info;否则的话就会渲染 Home */}
      <Route path="/" exact component={Info} />
      <Route path="/home" component={Home} />
      <Route path="/about" component={About} />
    </Switch>
    
    1
    2
    3
    4
    5
    6
    7
  • 作用是可以将 Route 组件分组。

  • 最常用的场景就是实现 404 页面渲染。

    <Switch>
      {/* 当访问一个不存在的路由时,就会渲染最下面的 NotFound 组件 */}
      <Route path="/" exact component={Info} />
      <Route path="/home" component={Home} />
      <Route path="/about" component={About} />
      <Route component={NotFound} />
    </Switch>
    
    1
    2
    3
    4
    5
    6
    7
  • 声明式的可访问导航。

  • to 属性,跳转路径,可以是字符串,也可以是对象。对象形式可以添加这些属性:pathname、search、hash、state。

    {/* 字符串 */}
    <Link to="/home">跳转到 Home</Link>
    
    {/* 对象 */}
    <Link to={{ pathname: "/home", search: "?name=abc" }}>跳转到 Home</Link>
    
    1
    2
    3
    4
    5
  • replace 属性,布尔类型,如果设置为 true,会替换当前的历史记录。

  • 可以理解成特殊的 Link,用法跟 Link 一样,只不过当匹配的时候可以添加样式。

  • 可以通过 activeClassName 或者 activeStyle 的方式添加样式。

    <NavLink activeStyle={{ color: 'red' }} to="/about">跳转到 About</NavLink>
    
    1
  • exact 属性,设置为 true 的话,表示只有严格匹配的时候才会应用设置的样式。

# 6. Redirect 组件

  • 重定向组件,必须有 to 属性。有以下常用属性:

    to 属性,可以是字符串或者对象。

    push 属性,布尔类型,为 true 的话表示会将新地址推入历史记录中,而不是替换。通过 history.push 实现的。

    from 属性

    exact 属性

  • 最常用的场景就是登录跳转。

    const isLogin = false; // 模拟未登录,当访问 /info 时就会重定向到 Home 页面
    
    return (
      <Route path="/info" render={() => { return isLogin ? <Info /> : <Redirect to="/home" />}} />
    )
    
    1
    2
    3
    4
    5

# 7. History 对象

  • 使用它的 push 方法可以实现编程式导航。

    import React from 'react';
    
    function Home(props) {
      function handleClick() {
        props.history.push('/about')
      }
    
      return (
        <div>
          <h1>Home</h1>
          <button onClick={handleClick}>跳转到 About</button>
        </div>
      )
    }
    
    export default Home;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 8. withRouter 组件

// App.js
import { withRouter } from 'react-router-dom';

function App(props) {
  // 使用了 withRouter 进行包裹之后,props 才有值
  console.log(props);

  return (
    ...
  )
}

export default withRouter(App);
1
2
3
4
5
6
7
8
9
10
11
12
13
// index.js
// BrowserRouter 必须在最外层,所以在这里让它将整个 App 包裹起来就行了
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
1
2
3
4
5
6
7
8
9
10

# 动态路由

  1. 指的是路由规则不是事先确定的,而是在渲染过程中确定的。比如:/about/1、/about/2... 这样的形式。

  2. 使用 : 就可以实现。

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

然后在 About 组件中可以通过 props.match.params.id 获取到参数。

import React from 'react';

function About(props) {
  return (
    <div>
      <h1>About</h1>
      <p>url 参数:{props.match.params.id}</p>
    </div>
  )
}

export default About;
1
2
3
4
5
6
7
8
9
10
11
12

# 嵌套路由

比如在 Home 组件中通过二级路由渲染 About 和 Info 组件。

import React from 'react';
import About from './About';
import Info from './Info';

import { Route, Link } from 'react-router-dom';

function Home(props) {
  function handleClick() {
    props.history.push('/about')
  }

  return (
    <div>
      <h1>Home</h1>
      <button onClick={handleClick}>跳转到 About</button>
      <div>
        <Link to={`${props.match.path}/one`}>二级路由 About</Link>
      </div>
      <div>
        <Link to='/home/two'>二级路由 Info</Link>
      </div>
      <Route path={`${props.match.path}/one`} component={About}></Route>
      <Route path='/home/two' component={Info}></Route>
    </div>
  )
}

export default Home;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
上次更新时间: 2023年05月23日 19:06:24