React Router V4 Recap and Demo

React Router V4 简介

最近很久没有React Router,好像上一次用还在用V3,最近被问了个关于react routerv4的小问题,怎么也想不起来了,决定自己看着文档,再重新demo一遍复习一下。

package

React Router主要包含三个包: react-router, react-router-dom,react-router-nativereact-router包含了主要的route component和功能。react-router-domreact-router-native提供了基于不同平台(web和mobile)的component(类似于react需要把react和react-dom库分开的原因,为了支持不同的平台)。

Note
但是react-router-domreact-router-native中都包含了react-router这个库,因此在项目中使用的时候只需要根据你的平台安装上react-router-dom或者react-router-native即可

The Router

本文基于浏览器做平台。首先你需要确定使用哪一种Router。
React Router中包含两种router(被分装成component)。

  • <BrowserRouter>
  • <HashRouter>

区别

  • HashRouter:

    使用URL中的hash部分保持URL和页面内容的同步。

    • 不支持location.key和location.state
  • BrowserRouter:

    URL始终和页面的UI同步

    • 针对于服务器可以server所有URL,并返回不同的页面

每一个Router组件只能接受一个child component,通常会创建一个App Component作为子组件render app剩下的部分。将应用从 router 中分离对服务器端渲染有重要意义,因为我们在服务器端转换到 <MemoryRouter> 时可以很快复用 <App> ??????

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))

History

每一个Router(通常一个SPA会有一个Router)都会创建一个History对象,可以将这个对象理解成一个Location object数组,用来存储浏览器URL的变化轨迹。虽然hostory记录着历史,但是你只能从中get到当前的Location object。

每当地址栏发生改变,history对象就会改变(history存储在react的context),因此Router component的update就开始了。保证了此时页面内容会随着每次URL的改变rerender。

因此Router组件必须是最外层组件。

Location Object

Location object反应的是你当前的地址信息,包含了很多从地址栏中分出的信息

{
  pathname: '/here',
  search: '?key=value',
  hash: '#extra-information',
  state: { modal: true }, //这个是可以attach到这个location上的数据,但是不会出现在URL中
  key: 'abc123' //这个location的唯一标志,也可以理解成数据存放的key
}

Routes

<Route> 组件是 React Router 的主要组成部分,如果你想要在路径符合的时候在任何地方渲染什么东西,你就应该创造一个 <Route> 元素。

What does the <Route> render?

每一个Route component 都必须有一个属性,能够描述当这个route被match的时候应该render些什么。以下三个属性可以用来描述render的内容。

component

当被match的时候,Route 组件会将会使用React.createElement创建一个component属性指定类型的React component。

render

属性的类型是一个function,这个function可以返回一个component(类似于react render props)。用于当你想要传递一些属性给被render的component的时候使用

上面两个属性必须使用一个
children

也是一个function return 一个component,对于上两个属性,只有route被匹配的时候才会被render。 这个属性,只要写了,不论任何情况这个component都会被render

Path

Route组件需要一个属性path,描述了这个route需要匹配的pathname(仅包含port之后search之前的部分)。比如<Route path='/roster'/> 能够匹配上的应该是pathname以/roster开头的所有URL,一旦match上相应的component就会被render

<Route path='/roster'/>
// when the pathname is '/', the path does not match
// when the pathname is '/roster' or '/roster/2', the path matches
// If you only want to match '/roster', then you need to use
// the "exact" prop. The following will match '/roster', but not
// '/roster/2'.
<Route exact path='/roster'/>
// You might find yourself adding the exact prop to most routes.
// In the future (i.e. v5), the exact prop will likely be true by
// default. For more information on that, you can check out this 
// GitHub issue:
// https://github.com/ReactTraining/react-router/issues/4958

Note:

  • react-router只会匹配pathname,也就意味着对于http://www.example.com/my-projects/one?extra=false,在react-router中等价于http://www.example.com/my-projects/one
  • react-router中route的位置,决定了send的URL匹配的是哪一个route。
  • 注意选择是否要加参数exact, 因为不使用exact的时候,只要URL使用path开头就会被匹配

render component props

对于任何一个被匹配的route,这个route被render的component一定能够接受三个属性:

  • location:描述当前的所在的URL

    hash: ""
    pathname: "/"
    search: ""
    state: undefined
    
  • history

    react router的Router创建的history对象,其中包含history API中包含的function,还有当前URL的location对象

  • match

    isExact: true
    params: {}
    path: "/"
    url: "/"
    

match object

当URL和某一个route match上的时候,会立刻创建一个match object

url 被match的URL的pathname
params path params (/a/b/:id中的id就是path params)
path route path属性的值
isExact 是否是exact的完全匹配(path === url)

Note: match的object存储在匹配的component的props中

使用browserRouter请求/roaster总是404

此时我的express是这样配置的:

const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.resolve(__dirname,'../','./dist')));

module.exports = app;

也就意味着,当请求/roaster 发送过来文件/dist/roaster一定不存在,所以导致404。

因为单页应用,必须要保证每一个URL发过来都应该返回index.html然后JS文件回来浏览器再处理route问题

const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.resolve(__dirname,'../','./dist')));

app.get('*', function (req,res) {
  res.sendFile(path.resolve(__dirname,'../','./dist/index.html'))
});

module.exports = app;

使用browserRouter请求/roaster/id JS file 404

仔细查看生成的HTML文件,发现<script>标签包住的JS的打包文件的地址不对类似于roaster/main-12324343543.js

原因是HTML webpack plugin生成HTML文件的时候,JS文件的请求地址是这样src=main-12312312.js(相对的),这种情况会导致请求HTML/roaster/id, js的请求文件路径直接替换最后slack的部分变成roaster/main-12324343543.js

解决只需要简单的在webpack加上publicPath

  output: {
    publicPath: '/',   
    path: __dirname + '/dist',
    filename: '[name]-[chunkhash].js'
  }

如何创建Nested Routes

由于Routes Component可以用在APP的任意位置,只要作为Router的child即可。因此你可以使用switch component将route combine成一个group。

function Main() {
  return (
    <main>
      <Switch>
        <Route path='/roster' component={Roster}/>
      </Switch>
    </main>
  );
}

function Roster() {
  return (
    <div>
      <h2>This is a roster page!</h2>
      <Switch>
        <Route exact path='/roster' component={FullRoster}/>
        <Route path='/roster/:number' component={Player}/>
      </Switch>
    </div>
  );
}
  • 先创建一个Main component将App的所有Route写进去
    • 所有用roster开头的URL都会match到Main的Roster component
  • 然后再Roster component中再细分/roaster/之后的path

但是尽管是嵌套路由,在Roaster Component中的Routes仍然需要写full path(是/roster/:numbe 而不是/:number

routes component传递data

React Router中使用route传递数据的方式有三种:

  • path variable:数据在URL中:

    • set:

          <Route path='/roster/:id' component={Player} />
      
          request:  /roster/111
      
    • get

      path variable一般存储在match object中,通过match.params即可获取一个object {id:111}

      match object在任意一个被match的route对应的component中都可以通过this.props.match获取

  • query parameters:数据在query中

    • set:

       ```
           <Route path='/roster' component={Player} />
      
           request:  /roster?a=1&b=2&c=3
       ```
      
    • get

    query parameters一般存储在location object中,通过location.search即可获取一个string "?a=1&b=2&c=3" 注意会带?

  • location.state: 数据在location object中,只有在react-router中可以使用。

    • set:由于Link的to属性以及history.push都能能够接受两种类型的值:string 和 location object。可以通过使用location object作为参数

        const location = {
            pathname: "/roster",
            search: "?a=1",
            state: {b:2}
        }
        
        <Link to={location} >roster</Link>
      

      一旦点击link就会render Roster component,在这个component中,通过location.state就可以获取{b:2}

写在最后

  1. 你可以创建一个没有path属性的Route component,这个Route会和所有URL match

关于history object

  1. history objects typically have the following properties and methods:

    • length - (number) The number of entries in the history stack
    • action - (string) The current action (PUSH, REPLACE, or POP)
    • location - (object) The current location. May have the following properties:
      • pathname - (string) The path of the URL
      • search - (string) The URL query string
      • hash - (string) The URL hash fragment
      • state - (object) location-specific state that was provided to e.g. push(path, state) when this location was pushed onto the stack. Only available in browser and memory history.
    • push(path, [state]) - (function) Pushes a new entry onto the history stack
    • replace(path, [state]) - (function) Replaces the current entry on the history stack
    • go(n) - (function) Moves the pointer in the history stack by n entries
    • goBack() - (function) Equivalent to go(-1)
    • goForward() - (function) Equivalent to go(1)
    • block(prompt) - (function) Prevents navigation (see the history docs)
  1. history对象是可变的,也就是说整个App的history对象只有一个,直接操作它会比较危险。如果你只是想获取location的信息,可以在component中使用this.props.location获取,而不是this.props.history.location

    class Comp extends React.Component {
      componentWillReceiveProps(nextProps) {
        // location都会创建新的
        const locationChanged = nextProps.location !== this.props.location
        // always true
    
        // 而history中的location永远只维护一个,每次都是在这上面修改
        const locationChanged = nextProps.history.location !== this.props.history.location
      }
      // always false
    

}

```

**Note: location is also found on history.location but you shouldn’t use that because its mutable.**

https://blog.pshrmn.com/entry/a-little-bit-of-history/#anno-5

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • React Router 4.0 (以下简称 RR4) 已经正式发布,它遵循React的设计理念,即万物皆组件。所...
    world_7735阅读 1,502评论 0 2
  • React-Router v4 1. 设计理念1.1. 动态路由1.2. 嵌套路由1.3. 响应式路由 2. 快速...
    wlszouc阅读 8,543评论 0 14
  • React Router 4.0 (以下简称 RR4) 已经正式发布,它遵循React的设计理念,即万物皆组件。所...
    梁相辉阅读 97,320评论 24 195
  • react-router 本来想给大家教学react-router2.0的版本。但是考虑到4.0的版本已经出现了。...
    Kris_lee阅读 5,583评论 0 4
  • [一]声啼哭划长空 [剪]脐伴母一女生 [梅]兰竹菊喜君心 [生]辰正是雪迎春 [日]月争辉送吉言 [快]意人生扬...
    一剪梅WHX阅读 702评论 1 3