如果你想理解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-string,querystring与原生的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内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

浅谈React Router关于history的那些事的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  5. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  6. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  7. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  8. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  9. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  10. 如何为iOS的React Native设置分析

    所以我已经完成了一个针对iOS的ReactNative项目,但是我想在其中分析.我尝试了react-native-google-analytics软件包,但是问题阻止了它的正常工作.此外,react-native-cordova-plugin软件包只适用于Android,因此插入Cordova插件进行分析的能力现在已成为问题.我也没有Swift/ObjectiveC的经验,所以将完全失去GA的插入.有没有人有任何建议如何连接GoogleAnalytics的ReactNativeforiOS?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部