react-router原理

前端路由的原理

自己来监听URL的改变,改变url,渲染不同的组件(页面),但是页面不要进行强制刷新(a元素不行)。

  • hash模式,localhost:3000/#/abc
    • 优势就是兼容性更好,在老版IE中都可以运行
    • 缺点是有一个#,显得不像一个真实的路径
  • 通过HTML5中的history修改URL

1. URL的hash

原理:通过监听hashchange变化

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <a href="#/home">首页</a>
      <a href="#/about">关于</a>

      <div class="router-view"></div>
    </div>
    <script>
      // 获取router-view的DOM
      const routerViewEl = document.getElementsByClassName("router-view")[0];

      // 监听URL的改变
      window.addEventListener("hashchange", () => {
        switch (location.hash) {
          case "#/home":
            routerViewEl.innerHTML = "首页";
            break;
          case "#/about":
            routerViewEl.innerHTML = "关于";
            break;
          default:
            routerViewEl.innerHTML = "";
        }
      });
    </script>
  </body>
</html>

2. HTML5的history

HTML5 的 History API 是一组用于管理浏览器历史记录的接口,它允许您通过 JavaScript 动态地操纵浏览器历史记录,以便在不刷新页面的情况下实现页面内容的变化和导航

前端路由不管是hash模式还是history模式,本质都是改变URL不能刷新页面

History API 提供了以下几个主要方法和属性:

  1. pushState():向浏览器历史堆栈中添加新的状态。
  2. replaceState():替换当前状态而不向历史堆栈中添加新状态。
  3. state:表示当前历史状态的对象。
  4. popstate 事件:当用户点击浏览器的前进或后退按钮时触发。

以下是一个简单的示例,演示如何使用 History API 在页面中切换内容而不刷新页面:

// 添加新状态到历史记录
history.pushState({ page: 1 }, "Page 1", "/page1");

// 替换当前状态
history.replaceState({ page: 2 }, "Page 2", "/page2");

// 监听 popstate 事件
window.addEventListener("popstate", function(event) {
  console.log("Location: " + document.location + ", State: " + JSON.stringify(event.state));
});

// 触发 popstate 事件(模拟用户点击浏览器的前进或后退按钮)
history.back();

通过使用 History API,您可以在不刷新页面的情况下改变 URL 和页面内容,使得单页应用程序(SPA)和其他现代 Web 应用程序能够更好地管理用户导航和状态。

history.pushState() 方法中,有三个参数,它们分别表示:

  1. state:这是一个表示新状态的对象,可以包含任意数据。当用户导航到新状态时,可以通过 event.state 来访问这个对象。在您的示例中,{ page: 1 } 表示新状态的页面为第一页。

  2. title:这是一个现在已经被忽略的参数,通常传入一个空字符串即可。之前这个参数用于指定新状态的标题,但现在大多数浏览器都忽略了这个值。

  3. URL:这是新状态的 URL 地址,表示用户导航到的新页面。在您的示例中,"/page1" 表示用户导航到的页面为 /page1

总结起来,在 history.pushState({ page: 1 }, "Page 1", "/page1"); 中,第一个参数 { page: 1 } 是新状态的数据对象,第二个参数 “Page 1” 是标题(已忽略),第三个参数 “/page1” 是新状态的 URL 地址。

另外,
在执行 history.pushState() 方法时,不会立即触发 popstate 事件。popstate 事件是在用户点击浏览器的前进或后退按钮时,或通过 JavaScript 调用 history.back()history.forward()history.go() 方法时触发的。

当您使用 history.pushState() 方法添加新状态到浏览器历史堆栈时,浏览器会将新状态添加到历史记录中,但不会立即触发 popstate 事件。只有当用户通过浏览器的导航操作改变历史记录时,才会触发 popstate 事件,以便您可以监听并响应这些历史记录的变化。

因此,执行 history.pushState() 不会直接触发 popstate 事件,而是在用户进行浏览器历史记录导航操作时才会触发该事件。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <a href="/home">首页</a>
      <a href="/about">关于</a>

      <div class="router-view"></div>
    </div>

    <script>
      // 1.获取router-view的DOM
      const routerViewEl = document.getElementsByClassName("router-view")[0];

      // 获取所有的a元素, 自己来监听a元素的改变
      const aEls = document.getElementsByTagName("a");
      for (let el of aEls) {
        el.addEventListener("click", (e) => {
          e.preventDefault();
          const href = el.getAttribute("href");
          history.pushState({}, "", href);
          urlChange();
        });
      }

      // 执行浏览器 前进/后退 操作时, 依然来到urlChange
      window.addEventListener("popstate", urlChange);

      // 监听URL的改变
      function urlChange() {
        switch (location.pathname) {
          case "/home":
            routerViewEl.innerHTML = "首页";
            break;
          case "/about":
            routerViewEl.innerHTML = "关于";
            break;
          default:
            routerViewEl.innerHTML = "";
        }
      }
    </script>
  </body>
</html>

react-router

react-router的版本4开始,路由不再集中在一个包进行管理了,而是进行了分包:

  • react-router是router的核心代码
  • react-router-dom是用于浏览器的
  • react-router-native是用于RN应用的

提示:安装react-router-dom会自动安装react-router

1. react-router基础

在这里插入图片描述

2. NavLink

在这里插入图片描述

NavLink是对Link的一个封装,支持更多属性,例如exact、activeStyle、activeClassName(这个属性的默认值为active)等

3. 动态路由
<NavLink to={`/detail/${id}`} activeClassName="link-active">详情</NavLink>
// const match = this.props.match;
// console.log(match.params);

<NavLink to={`/detail2?name=xiaoming&age=18`} activeClassName="link-active">详情2</NavLink>
// <h2>Detail2: {this.props.location.search}</h2>
<NavLink to={{
          pathname: "/detail3",
          search: "name=abc",
          state: info
         }} 
        activeClassName="link-active">
  详情3
</NavLink>
// to后面的对象将被保留到this.props.location中
// <h2>Detail3: {this.props.location.state.name}</h2>
4. redirect
import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom';

export default class User extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      isLogin: true
    }
  }
  render() {
    return this.state.isLogin ? (
      <div>
        <h2>User</h2>
        <h2>用户名: coderwhy</h2>
      </div>
    ): <Redirect to="/login"/>
  }
}
5. 嵌套路由(19-45分)

在这里插入图片描述
对于嵌套路由,为了避免子路由里重父组件上的路径,可以通过basename处理,参考https://v5.reactrouter.com/web/api/BrowserRouter/basename-string

在这里插入图片描述

6. 手动路由跳转(19-59)
// App.jsx
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:id" component={User} />

因为About组件是在Route的component里渲染的,所以Route可以给About组件灌入很多属性,包括history、match等

// About.jsx
export default class About extends PureComponent {
  render() {
    console.log(this.props.route);
    const branch = matchRoutes(this.props.route.routes, "/about");
    console.log(branch);

    return (
      <div>
        <NavLink exact to="/about" activeClassName="about-active">企业历史</NavLink>
        <NavLink exact to="/about/culture" activeClassName="about-active">企业文化</NavLink>
        <NavLink exact to="/about/contact" activeClassName="about-active">联系我们</NavLink>
        <button onClick={e => this.jumpToJoin()}>加入我们</button>

        <Switch>
          <Route exact path="/about" component={AboutHisotry}/>
          <Route path="/about/culture" component={AboutCulture}/>
          <Route path="/about/contact" component={AboutContact}/>
          <Route path="/about/join" component={AboutJoin}/>
        </Switch>
      </div>
    )
  }
  jumpToJoin() {
    // console.log(this.props.history);
    // console.log(this.props.location);
    // console.log(this.props.match);
    this.props.history.push("/about/join");
  }
}
withRouter (19 - 1:21)

源码阅读:react-router-dom再调用了react-router
在这里插入图片描述

7. 路由统一管理react-router-config

对于上面的嵌套路由显得太零散,因此需要对路径进行统一管理。

// router.js
import Home from '../pages/home';
import About, { AboutHisotry, AboutCulture, AboutContact, AboutJoin } from '../pages/about';
import Profile from '../pages/profile';
import User from '../pages/user';

const routes = [
  {
    path: "/",
    exact: true,
    component: Home
  },
  {
    path: "/about",
    component: About,
    // 子路由
    routes: [
      {
        path: "/about",
        exact: true,
        component: AboutHisotry
      },
      {
        path: "/about/culture",
        component: AboutCulture
      },
      {
        path: "/about/contact",
        component: AboutContact
      },
      {
        path: "/about/join",
        component: AboutJoin
      },
    ]
  },
  {
    path: "/profile",
    component: Profile
  },
  {
    path: "/user",
    component: User
  }
]
export default routes;
// App.js
import { renderRoutes } from 'react-router-config';
import routes from './router';
render() {
	return (
		<div>{renderRoutes(routes)}</div>
	)
}
// About.js
import { renderRoutes } from 'react-router-config';
render() {
    return (
      <div>
        <NavLink exact to="/about" activeClassName="about-active">企业历史</NavLink>
        <NavLink exact to="/about/culture" activeClassName="about-active">企业文化</NavLink>
        <NavLink exact to="/about/contact" activeClassName="about-active">联系我们</NavLink>
        <button onClick={e => this.jumpToJoin()}>加入我们</button>

        {/* <Switch>
          <Route exact path="/about" component={AboutHisotry}/>
          <Route path="/about/culture" component={AboutCulture}/>
          <Route path="/about/contact" component={AboutContact}/>
          <Route path="/about/join" component={AboutJoin}/>
        </Switch> */}
		// 因为父组件是通过renderRoutes渲染的,所以能够拿到this.props.route
        {renderRoutes(this.props.route.routes)}
      </div>
    )
  }

react-router-config

参考链接:react-router

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

. . . . .

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值