谈谈history

react router更新迭代很快,但是总感觉一切事物都万变不离其宗,很好的掌握了history这个对象,能在工作的时候更得心应手。翻译了一篇文章,如果有什么不通之处希望指出。

原文

想要很好理解react router,首先必须要学会history。进一步讲,history包提供了react router需要的核心功能。它使得单页面应用能容易的根据导航改变location

npm install –save history

history包导出了三个方法,分别对应三种类型的history:browserhashmemory

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

React router自动帮我们创建了history对象,所以我们不必自己去直接操作history。但是,理解它们可以让我们知道其中哪个更适合我们的项目。

What is history?(什么是history)

无论你创建什么类型的history,最后都将得到一个拥有相同属性和方法的对象

Location

locationhistory中最重要的属性,表明了应用当前’在哪’,它包含了许多衍生自URL的属性,比如pathnamesearchhash

并且,每个location对象都具有一个单一的key属性用来区分当前location。

最后,location还包含一个state属性提供URL没有展示的附加数据,即相关参数变量。

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

首先需要一个原始的location来用于创建history对象,每种类型的history的原始location不同,例如,browser history会解析当前URL

One location to rule them all?(仅仅一个location来规范所有的功能么?)

虽然我们每次只能访问一个位置,但是history会追踪维护每个访问过的location。正是这种可增加location并且可在整个location中穿梭的能力使得history成为”history”。如果history仅仅关心当前location,那么它就要改名字叫”present”了。

除了location数组,history还维护了一个当前location关联到数组中的索引值。
memory history中,这些都已经被定义好了,browserhash histories中,location数组和索引值是被browser控制的,不能直接被操作。

Navigation

仅仅是包含一个location的属性远远不够。navigation方法才真正让history变得有趣起来。这些方法可以操控改变当前的location

Push

push方法允许我们定位到一个新的location。它将在location数组尾部添加一个新的location。当调用push的时候,“future” locations(因为使用后退按钮生成的当前定位location后面的locations)就会被舍弃。
当你点击Link的时候,默认使用的就是history.push方法。

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

Replace

和push方法相似,但是它会从数组中替换掉当前location,“Future” locations不会被删除。React router<Redirect>就应用了replace

例如,从当前页面点击链接跳转到第二页面,第二页面会重新定向到第三个页面。如果使用push方法,那么在第三页面点击后退按钮的时候,会返回到第二页面,然后又重新跳转到第三页面,循环往复。但是如果使用replace,那么后退返回的就是第一页面。

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

Go, goBack, goForward

goBack
返回某一页面,实际上是减小了locations数组中的定位索引。

history.goBack()

goForward与之相反,向前跳一个页面。仅仅当** future locations **存在时才会起作用。

history.goForward()

go是两者的结合体,更加强大。负数参数用来后退,正数参数用来向前。

history.go(-3)

Listen

history使用观察者模式,外部代码可以监听location的变化。

每个history对象都有listen方法,接收一个方法作为参数。这个方法将被加到history维护的监听函数序列中。任何时候location改变(无论是代码控制改变还是用户点击浏览器按钮),history对象都会调用所有的监听函数。这样在location改变时就可以做相关代码操作。

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

React Router 的路由组件会监听自己的history对象,这样当location改变的时候就可以re-render了。

Linking things together

每个history都有个createHref方法,接收location对象转化成URL
例如<a>元素并不能正确解析history对象,也无法理解什么是location。所以为了让HTML在无法解析history的情况下正确导航,我们需要将其转化为真正的URLs。

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如何工作了。

With our powers combined

三种类型的history还是有些不同的,在使用过程红需要我们自己去斟酌到底什么类型的才比较适合我们的项目。下面对比不同的使用场景。

In the Browser

browser和hash histories一般应用于浏览器。它们与historylocationweb API交互从而保证当前浏览器地址栏展示的地址和当前location一致。

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

上述两种history最大的区别是根据URL创建location的方式。browser history会根据完整URL创建,但是hash根据hash符号#后面的部分URL创建。

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

Hashing things out

既然这样,为什么还会有人用hash history呢?

当你导航到一个URL的时候,理论上服务器会有一个对那个的文件被访问。
对于动态服务器来讲,被访问文件不一定真的存在。服务器会检测访问URL并决定返回什么HTML。但是对于静态文件服务器来讲,它仅仅能返回磁盘已存在的文件。它们能做的就是提供URL的时候返回这些匹配的index.html文件。

鉴于静态服务器的局限性,最简单粗暴的解决方式就是被访问服务器具有'real' location来对应访问的HTML。当然,对于我们的应用来讲一个location对应一个URL,这样history的使用将会毫无意义。为了突破这种局限性,hash history 使用URL的hash部分设置读取location。

//假如example.com 使用静态服务器, 这些URLs访问的都是/my-site/index.html
http://www.example.com/my-site#/one
http://www.example.com/my-site#/two
// 但是, 如果使用hash history, 应用的URL就会变得不同,URL的hash部分会派生出不同的URLs。
{ pathname: '/one' }
{ pathname: '/two' }

基于hash的特性,它最大的用途就是当我们使用静态服务器的时候用于获取HTML。

Memory: The Catch-all History

Memeory 最大的好处就是任何能用js的地方都可以使用它。

例如可以在Node环境的单元测试中使用它。这样可以直接依赖history对象测试代码而不需要在浏览器中运行。
并且,它也可以应用于手机端。被react-router-native调用的memory history是基于react-native应用的导航来实现location功能的。
甚至它也可以应用在浏览器中(但是地址栏会丢失相应的地址信息)。

它和前两者者最大的不同是它维护的是自身内部的location数组。memory history创建之初就可以设置原始数据,这些数据包含了location数组和当前location索引,而不必像其他二者一样依赖浏览器存储的locations。

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

自己实现history功能的时候,会遇到不少浏览器操作导航的坑,最简单的方法就是依赖现成的history来解决问题。
无论用哪种类型的history,最终目的都是为了更好的实现应用中的导航功能。

https://juejin.im/post/5dfaebf6f265da33d912e0c4

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论