浅谈React Router关于history的那些事

如果你想理解React Router,那么应该先理解history。更确切地说,是history这个为React Router提供核心功能的包。它能轻松地在客户端为项目添加基于location的导航,这种对于单页应用至关重要的功能。

npm install --save history

存在三类history,分别时browser,hash,与 memory。history包提供每种history的创建方法。

import {
 createBrowserHistory,
 createHashHistory,
 createMemoryHistory
} from 'history'

如果你使用React Router,他会为你自动创建history对象,所以你并不需要与history进行直接的交互。不过,理解不同类型的history依旧很重要,这样你能在项目中决定究竟是用哪个。

history是什么?

无论你创建哪种history,你最终都会得到一个几乎拥有相同属性与方法的对象。

location

history对象中最重要的属性就是location。location对象反映了当前应用所在的"位置"。其包含了pathname,search[注1],hash这种由'URL'派生出的属性。

此外,每一个location都拥有一个与之关联且独一无二的key。'key'用于特定location的识别,向特定location存储数据。

最后,location可以拥有与之相关的状态。这是一些固定的数据,并且不存在于URL之中。

{
 pathname: '/here',
 search: '?key=value',
 hash: '#extra-information',
 state: { modal: true },
 key: 'abc123'
}

当创建一个history对象后,需要初始化location。对于不同类型history这一过程也不相同。例如,browser history会解析当前URL。

一个location控制所有?

诚然我们只能访问当前location,history对象持续追踪着一组location。正因为拥有添加location并能够访问数组中任意location的能力,history才能被称为“历史”。如果history只能记录当前location,那就应该叫它“present”。

除了一组location外,history也保存一个索引值,用来指向当前所对应的location。

对于memory history,它们被直接定义。而对于browser history与hash history,数组与索引被浏览器所控制,并不能直接访问[注2]。

navigation方法
可以说navigation方法是拥有location属性的history体系的点睛之笔。navigation允许你改变当前location。

push方法
push方法使能你跳转到新的location。通过在当前location后添加新的location时,任意的'未来'location会被清除(之前由后退按钮而形成的在当前location后的location)。

默认情况下,当你点击<Link>时,会调用history.push方法进行导航。

history.push({ pathname: '/new-place' })

replace
replace方法与push相似,但它并非添加location,而是替换当前索引上的位置。'未来'location将不会被清除。

重定向时要使用replace。这也是React Router的<Redirect>组件中使用的方法。

例如,当你在页面1通过点击link按钮导航到页面2,页面2可能会重定向到页面3。如果使用push方法,点击放回按钮将从页面3返回到页面2(这里有潜在的可能再重定向到页面3)。如果使用replace方法,会从页面三直接返回页面1。

history.replace({ pathname: '/go-here-instead' })

go, go, go
最后有三个带‘go'的方法,它们分别是goBack,goForward与go。
goBack返回一层页面。实际上是将history的索引值减1。

history.goBack()

goForward与goBack相对。向前一层页面。这仅在拥有'未来'location生效,即当用户点击了后退按钮。

history.goForward()

go是一个强大的方法,并包含了goForward与goBack的功能。传入负数则退后,传入正数则向前。

history.go(-3)

监听!

采用观察者模式,在location改变时,history会发出通知。每一个history对象都有listen方法,接受一个函数作为参数。这个函数会被添加到history储存的监听函数数组中。当location变化时(如代码调用history方法或用户点击浏览器按钮),history对象将会调用所有listener方法。这能让你在location变化时来设置代码更新。

const youAreHere = document.getElementById('youAreHere')
history.listen(function(location) {
 youAreHere.textContent = location.pathname
})

React Router的router组件将会订阅history对象,这样当location变化时,其能重新渲染。

链接事物Linking things together

每一类history都拥有createHref方法,其使用location对象,输出URL。
内部,history通过location对象进行导航。然而像锚点元素(a),它并不知道history这个包,也不知道location对象是什么。为了能让生成的HTML 在不需要history的情况下,依旧能够导航。我们必须生成真的URL。

const location = {
 pathname: '/one-fish',
 search: '?two=fish',
 hash: '#red-fish-blue-fish'
}
const url = history.createHref(location)
const link = document.createElement('a')
a.href = url
// <a href='/one-fish?two=fish#red-fish-blue-fish'></a>

以上涵盖了基础的history API。虽然还有其他未介绍的属性与方法,但上述方法能够是你明白history对象是如何运作的。

结合在一起

不同类型的history间还是存在差异的,这需要你去考虑选择一个适合你项目的history。
Between the three of them, any use case should be covered.

在浏览器中

browser history与hash history都被用于浏览器环境。它们与history和location的web API进行交互,因此当前location与浏览器地址栏中展示的是相同的。

const browserHistory = createBrowserHistory()
const hashHistory = createHashHistory()

它们两者的最大区别在于从URL创建location的方式。browser history使用完整URL[注3],而hash history只使用在第一个hash后的那部分URL。

// 提供如下URL
url = 'http://www.example.com/this/is/the/path?key=value#hash'
// browser history创建的location对象:
{
 pathname: '/this/is/the/path',
 search: '?key=value',
 hash: '#hash'
}
//hash history创建的location对象:
{
 pathname: 'hash',
 search: '',
 hash: ''
}

使用哈希

为何你需要hash history?理论上来说当你导航到一个URL时,服务端必有一个相应文件与之对应。对于动态服务,请求文件并不需要真实存在。相反,服务端会检查请求的URL并决定返回的HTML。

然而,静态文件服务可以直接返回存在磁盘中的文件。静态服务能做的最动态的事就是当URL制定目录时,从目录中返回index.html文件。

由于静态文件服务的这种限制,最简单的解决方案[注4]就是在服务端仅使用一个真实的location来返回用户端的获取需求。当然,仅有一个location意味着你的应用只有一个URL,这样就无法使用history。为了解决这一问题,hash history使用使用URL的哈希部分来读写location。

// 如果 example.com 使用静态资源服务, 这三个URL都将从
// /my-site/index.html获取相同数据
http://www.example.com/my-site#/one
http://www.example.com/my-site#/two
// 然而由于使用hash history,应用中三者的location是不同的,
// 因为location取决于URL的哈希部分
{ pathname: '/one' }
{ pathname: '/two' }

纵然hash history运作良好,但由于其依赖将所有路径信息存在URL的哈希中,它被认为有可能遭到黑客攻击。因此当网站没有动态服务时再考虑使用它吧。

memory:缓存所有history

使用memory location最棒的体验就是你可以在能使用JavaScript的地方随意使用。

一个简单的例子你可以通过运行Node在单元测试中使用它。这允许你能在不依赖浏览器运行的情况下测试代码。

更牛逼的是,memory history可以被使用在app中。在react-nativeapp中react-router-native使用memory history来实现基于location的导航。

你可以在浏览器中使用使用memory history,如果你愿意的话。(虽然这样你会失去与地址栏的交互能力)。

这memory history与其他两类history最大的区别在于其维护着自己的location。当创建memory history后你可以传入信息进行初始化状态。这个状态是一个location数组与当前location的索引[注5]。这与其他两类history是不同的,它们依赖浏览器来存储这个location数组。

const history = createMemoryHistory({
 initialEntries: ['/', '/next', '/last'],
 initialIndex: 0
})

使用history代替你来处理哪些相对繁琐且易错的是一个行之有效的方法。

无论你选择了何种类型的history,他们都是极易使用,并且拥有强大的能力进行导航与基于loaction的渲染。

注释

[1] search属性是一个字符串而非被解析对象。由于大多数字符串解析包在使用上各不相同。所以history把选择权留给了开发者而不是强制使用某种字符串解析包。如果你想了解更多,这里推荐一些流行的:query-stringquerystring与原生的URLSearchParams

[2] 这是出于安全性的限制。在浏览器中history的location数组不仅包涵了访问过的location信息。如果开放浏览会泄漏使用者的浏览器历史信息,因此无法开放访问。

[3] m默认情况下,browser history创建的location对象,它的路径名是URL全路径名。当然,你可以为history定一个基础名,这样路径名中的这部分将会被忽略。

const history = createBrowserHistory({ basename: '/path' })
// 给出的路径 url: http://www.example.com/path/here
// history对象将会创建如下location
{ pathname: '/here', ... }

[4] 理论上,可以让应用中的每个有效URL返回相同的HTML文件。虽然这可以事项,但如果所有的URL都是静态的,会产生大量冗余文件。不过任意地址都使用参数大量来匹配大量可能址是不可行的。
[5] 如果并未提供memory history的初始化location数组与索引,则会生成如下默认值:

entries = [{ pathname: '/' }]
index = 0

对于大部分应用这已经足够好了,但提前写入history对于恢复内容还是一个非常有用的方法。

到此这篇关于浅谈React Router关于history的那些事的文章就介绍到这了,更多相关React Router history内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何使用Redux Toolkit简化Redux

    了解Redux Toolkit,这是用于高效Redux开发的经过验证的工具集.在本文中,你将看到为什么Redux Toolkit值得React社区更多的关注. React和Redux被认为是大规模React应用中管理状态的最佳组合.然而,随着时间的推移,Redux的受欢迎程度下降,原因是: 配置Redux Store并不简单. 我们需要几个软件包来使Redux与React一起工作. Redux需要太多样板代码. 带着这些问题,Redux的创建者Dan Abramov发表了名为<你可能不需要Red

  • React+Ant Design开发环境搭建的实现步骤

    基础知识 1.使用脚手架创建项目并启动 1.1 安装脚手架: npm install -g create-react-app 1.2 使用脚手架创建项目: create-react-app antd-start-demo antd-start-demo为项目名. 1.3 启动 npm start 2.npm转换为yarn 2.1 安装yarn: npm install -g yarn ​ 2.2 获取yarn当前的镜像源: yarn config get registry ​ 2.3 设置为淘宝

  • Vite搭建React项目的方法步骤

    前言 日常放鸽,火钳刘明 这是一个基于 vite 搭建的 React 的项目,开发体验非常棒. 创建一个 Vite 项目 yarn create @vitejs/app 如上图,选择了 react-ts 预设模板,如果出现下图一样的工程 yarn // 安装依赖 yarn dev // 启动开发环境 打开浏览器输入http://localhost:3000/#/,如上图所示的话.那么恭喜你,你可以正常开发 React 项目了.完结撒花 如果不行的话,直接看 vite 官网,它比我写的详细 改造工

  • React利用路由实现登录界面的跳转

    上一篇在配置好了webpack和react的环境后,接下来开始写登录界面,以及接下来的跳转到主页的功能. 1.首先看一下总体的目录结构. 因为很多时候在看别人写的例子的时候因为目录结构不熟悉后边会出现意想不到的岔子. 2.大体流程: 1)webpack配置入口文件src/index.js 2)运行index.html后首先加载入口文件src/index.js 3)加载路由表src/router/index.js 4)根据路由表中的配置会首先加载登录界面src/login.js 5)当在登录界面登

  • react获取input输入框的值的方法示例

    react 获取input 输入框的值的多种方式 第一种方式 非受控组件获取 第二种方式 受控组件获取 非受控组件获取 ref import React , {Component} from 'react'; export default class App extends Component{ search(){ const inpVal = this.input.value; console.log(inpVal); } render(){ return( <div> <input

  • react hooks入门详细教程

    State Hooks 案例: import { useState } from 'react'; function Example() { const [count, setCount] = useState(0); //count:声明的变量:setCount:改变count值的函数:0:count的初始值 return ( <div> <p>You clicked {count} times</p> <button onClick={() => set

  • react实现Radio组件的示例代码

    本文旨在用最清楚的结构去实现一些组件的基本功能.希望和大家一起学习,共同进步 效果展示: 测试组件: class Test extends Component { constructor(props) { super(props) this.state = { active:1 } } onGroupChange(value) { this.setState({ active: value }) } render() { return ( <div> <RadioGroup onChan

  • 聊一聊我对 React Context 的理解以及应用

    前言 Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有. 在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context. The vast majority of applications do not need to use content. If you want your application to be stable, don't use context

  • 使用 React 和 Threejs 创建一个VR全景项目的过程详解

    最近我在学习使用 React 配合 Three.js 来搭建一个可以浏览720全景图片的项目 实现的是加载一张 2:1 的720全景 分享一下我的创建过程 一.搭建框架并安装需要的插件 npx create-react-app parano // 创建一个 React 项目 npm install -S typescript // 安装 typescript,这个是类型辅助插件,与全景项目关系不大 npm install -S @types/three // 安装 typescript 支持的

  • 浅谈React Router关于history的那些事

    如果你想理解React Router,那么应该先理解history.更确切地说,是history这个为React Router提供核心功能的包.它能轻松地在客户端为项目添加基于location的导航,这种对于单页应用至关重要的功能. npm install --save history 存在三类history,分别时browser,hash,与 memory.history包提供每种history的创建方法. import { createBrowserHistory, createHashHi

  • 浅谈react.js 之 批量添加与删除功能

    最近做的CMS需要用到批量添加图片的功能:在添加文件的容器盒子内,有两个内容,分别是:添加按钮与被添加的选择文件组件. 结构分析: 被添加的组件,我们称为:UploadQiNiuFiles(七牛文件上传组件),含一个删除当前组件的删除按钮 添加按钮的事件 被添加组件存放的容器 做这个效果只需要明白三个方法的用途就OK: 直接绑定要删除组件的  deleteType(),它是调用删除index数量的方法  removeContent() //删除{qiniu}与{deleteQiNiu}内容,是把

  • 浅谈react路由传参的几种方式

    第一种传参方式,动态路由传参 通过设置link的path属性,进行路由的传参,当点击link标签的时候,会在上方的url地址中显示传递的整个url <Link to='/home?name=dx'>首页</Link> 如果想真正获取到传递过来的参数,需要在对应的子组件中 this.props.location.search 获取字符串,再手动解析 因为传参能够被用户看见,传递获取比较麻烦,所以不推荐 第二种传参方式,隐式路由传参 <Link to={{ pathname: '

  • 浅谈React 属性和状态的一些总结

    一.属性 1.第一种使用方法:键值对 <ClaaNameA name = "Tom" /> <ClaaNameA name = {Tom} /> <ClaaNameA name = {"Tom"} /> <ClaaNameA name = {[1,2,3]} />//数组 <ClaaNameA name = {FunctionNAme} /> //定义一个函数 2.第二种方法:三个点的展开对象形式 var

  • 浅谈react.js中实现tab吸顶效果的问题

    在react项目开发中有一个需求是,页面滚动到tab所在位置时,tab要固定在顶部. 实现的思路其实很简单,就是判断当滚动距离scrollTop大于tab距离页面顶部距离offsetTop时,将tab的position变为fixed. 在react中,我在state中设置一个navTop属性,切换这个属性的值为true或者false,然后tab标签使用classnames()这个方法来利用navTop的值添加类名fixed. 一开始我是这样写的: import cs from 'classnam

  • 浅谈react+es6+webpack的基础配置

    这是模块化开发.主流框架和最新版的ECMAScript语法规范的一个小demo 准备工作 安装 nodeJs 首先进入node官网,去下载最新版的nodeJs webpack 安装webpack npm install webpack -g 参数-g表示全局安装webpack,你在cmd命令中哪个文件夹下都可以使用webpack的命令,如果不加-g的话,是只可以在你安装webpack的目录下使用webpack这个命令 webpack 也有一个 web 服务器 npm install webpac

  • 浅谈React Native打包apk的坑

    RN的打包,大家可以根据官网一步一步来,但这里有几个地方注意,一下简单介绍: 生成一个签名密钥 在项目的目录下打开cmd命令窗口输入一下命令运行: keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 这条命令会要求你输入密钥库(keystore)和对应密钥的密码,然后设置一些发行相关的信息.最后它会生成一个叫做my-re

  • 浅谈react受控组件与非受控组件(小结)

    我们都知道,有许多的web组件可以被用户的交互发生改变,比如:<input>,<select>,或者是我现在正在使用的富文本编辑器.这些组件在日常的开发中很不显眼,我们可以很轻易的通过输入一些内容或者设置元素的value属性来改变组件的值.但是,因为React是单向数据流绑定的,这些组件可能会变得失控: 1.一个维护它自己state里的value值的<Input>组件无法从外部被修改: 2.一个通过props来设置value值的<Input>组件只能通过外部

  • 浅谈React深度编程之受控组件与非受控组件

    受控组件与非受控组件在官网与国内网上的资料都不多,有些人觉得它可有可不有,也不在意.这恰恰显示React的威力,满足不同规模大小的工程需求.譬如你只是做ListView这样简单的数据显示,将数据拍出来,那么for循坏与 {} 就足够了,但后台系统存在大量报表,不同的表单联动,缺了受控组件真的不行. 受控组件与非受控组件是React处理表单的入口.从React的思路来讲,作者肯定让数据控制一切,或者简单的理解为,页面的生成与更新得忠实地执行JSX的指令. 但是表单元素有其特殊之处,用户可以通过键盘

  • 浅谈React中组件逻辑复用的那些事儿

    基本每个开发者都需要考虑逻辑复用的问题,否则你的项目中将充斥着大量的重复代码.那么 React 是怎么复用组件逻辑的呢?本文将一一介绍 React 复用组件逻辑的几种方法,希望你读完之后能够有所收获.如果你对这些内容已经非常清楚,那么略过本文即可. 我已尽量对文中的代码和内容进行了校验,但是因为自身知识水平限制,难免有错误,欢迎在评论区指正. 1. Mixins Mixins 事实上是 React.createClass 的产物了.当然,如果你曾经在低版本的 react 中使用过 Mixins,

随机推荐