Skip to content

8. react router

r_wang edited this page Jul 14, 2020 · 4 revisions

React-router

对于 web 应用来说,有一个导航条是很常见的,它可以切换应用的视图

在老派的web应用中,更改应用显示的页面将由浏览器向服务器发出 HTTP GET 请求并显示表示返回视图的 HTML 来完成。

在单页应用中,我们实际上总是在同一页上。 浏览器运行的 Javascript 代码会产生不同“页面”的错觉。 如果 HTTP 请求是在切换视图时发出的,那么它们只用于获取 JSON 格式的数据,新视图可能需要这些数据才能显示出来。

1. 没有路由的情况下可否实现切换页面呢
import React, { useState } from 'react'

const Home = () => (
  <div> <h2>TKTL notes app</h2> </div>
)

const Notes = () => (
  <div> <h2>Notes</h2> </div>
)

const Users = () => (
  <div> <h2>Users</h2> </div>
)

const App = () => {
  const [page, setPage] = useState('home')

 const toPage = (page) => (event) => {
    event.preventDefault()
    setPage(page)
  }

  const content = () => {
    if (page === 'home') {
      return <Home />
    } else if (page === 'notes') {
      return <Notes />
    } else if (page === 'users') {
      return <Users />
    }
  }

  const padding = {
    padding: 5
  }

  return (
    <div>
      <div>
        <a href="" onClick={toPage('home')} style={padding}>
          home
        </a>
        <a href="" onClick={toPage('notes')} style={padding}>
          notes
        </a>
        <a href="" onClick={toPage('users')} style={padding}>
          users
        </a>
      </div>
      {content()}
    </div>
  )
}

每个视图都作为自己的组件实现。 我们将视图组件信息存储在名为page 的应用状态中。 这个信息告诉我们,表示视图的哪个组件应该显示在菜单栏下面。

缺点:

  • 地址保持不变
  • 无法回退
  • 应用变得庞大之后,这套逻辑维护起来也异常麻烦
  • 无法添加书签

...

所以需要一套路由来管理

2. React Router

2.1 简单使用效果

2.2 实际上讨论的是BrowserRouter ,因为这里的导入是通过重命名导入的对象实现的

import {
  BrowserRouter as Router, //重命名
  Switch, Route, Link
} from "react-router-dom"

BrowserRouter 是一个 Router,它使用 HTML5的history API (pushState、 replaceState 和 popState 事件)保持 UI 与 URL 同步。

通常,当地址栏中的 URL 发生更改时,浏览器会加载一个新页面。 然而,借助于HTML5 history APIBrowserRouter,可以使用浏览器地址栏中的 URL 在 React-application 中进行内部“路由”。 因此,即使地址栏中的 URL 发生了变化,页面的内容也可以通过 Javascript 来操作,浏览器也不会从服务器加载新的内容。 使用后退和前进操作,以及制作书签,仍然像在传统网页上一样合乎逻辑。

2.3 History介绍

history是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。具体来说里面的history分为三类

  • 老浏览器的history: 主要通过hash来实现,对应createHashHistory

  • 高版本浏览器: 通过html5里面的history,对应createBrowserHistory

  • node环境下: 主要存储在memeory里面,对应createMemoryHistory

// 内部的抽象实现
function createHistory(options={}) {
  ...
  return {
    listenBefore, // 内部的hook机制,可以在location发生变化前执行某些行为,AOP的实现
    listen, // location发生改变时触发回调
    transitionTo, // 执行location的改变
    push, // 改变location
    replace,
    go,
    goBack,
    goForward,
    createKey, // 创建location的key,用于唯一标示该location,是随机生成的
    createPath,
    createHref,
    createLocation, // 创建location
  }
}
  • 执行URL前进
  • 检测URL回退

本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新。目前单页面使用的路由就只有两种实现方式

  • hash 模式
  • history 模式

2.4 react-router 原理

实现URLUI界面的同步。其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成locationcomponents之间的同步问题

react-routerhistory库的基础上,实现了URLUI的同步,分为两个层次来描述具体的实现。

react-router中最主要的componentRouter RouterContext Linkhistory库起到了中间桥梁的作用

  • browserHistory进行路由state管理,主要通过sessionStorage
//保存 路由state(router state) {route: '/about'} 
function saveState(key, state) {
  ...
  window.sessionStorage.setItem(createKey(key), JSON.stringify(state));
}
//读取路由state
function readState(key) {
  ...
  json = window.sessionStorage.getItem(createKey(key));
  return JSON.parse(json);
}

进行路由匹配,最终渲染对应的组件

3.1 安装

  • react-router React Router 核心
  • react-router-dom 用于 DOM 绑定的 React Router
  • react-router-native 用于 React Native 的 React Router
  • react-router-redux React Router 和 Redux 的集成
  • react-router-config 静态路由配置的小助手

react-router 还是 react-router-dom?

后者比前者多出了 <Link> <BrowserRouter> 这样的 DOM 类组件,安装后者就可以了

npm install --save react-router-dom

3.2 基本用法

  • BrowserRouter
  • HashRouter
histroy 属性

Router组件的history属性,用来监听浏览器地址栏的变化,并将URL解析成一个地址对象,供 React Router 匹配。

history属性,一共可以设置三种值。

  • browserHistory: 用于支持HTML5历史API的现代Web浏览器

  • hashHistory: 用于希望将位置存储在当前URL的哈希中以避免在页面重新加载时将其发送到服务器的情况

  • createMemoryHistory: 用作引用实现,也可用于非DOM环境,如对本地或测试的反应(测试代码中可能会用到)

每个history对象都有以下属性:

  • history.length - 历史堆栈中的条目数
  • history.location - 当前的 location
  • history.action - 当前导航操作
history.push(path, [state])

history.replace(path, [state])

history.go(n)

history.goBack()

history.goForward()

history.canGo(n) (only in createMemoryHistory)

3.2.1 使用BrowserRouter

BrowserRouter 是一个 Router,它使用 HTML5的history API (pushState、 replaceState 和 popState 事件)保持 UI 与 URL 同步。

  • 简单使用

  • 参数化

  • 重新定向