React 路由

一、路由使用起步配置

安装两个依赖:

npm install --save react-router
npm install --save react-router-redux

配置路由的时候参考npm文档:

https://www.npmjs.com/package/react-router-redux

官方的一个小 demo :

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, browserHistory } from 'react-router'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
 
import reducers from '<project-path>/reducers'
 
// Add the reducer to your store on the `routing` key
const store = createStore(
  combineReducers({
    ...reducers,
    routing: routerReducer
  })
)
 
// Create an enhanced history that syncs navigation events with the store
const history = syncHistoryWithStore(browserHistory, store)
 
ReactDOM.render(
  <Provider store={store}>
    { /* Tell the Router to use our enhanced history */ }
    <Router history={history}>
      <Route path="/" component={App}>
        <Route path="foo" component={Foo}/>
        <Route path="bar" component={Bar}/>
      </Route>
    </Router>
  </Provider>,
  document.getElementById('mount')
)

在 React 应用中,通常会用 <Router /> 包裹 <Route />。 如此,当 URL 变化的时候,<Router /> 将会匹配到指定的路由,然后渲染路由绑定的组件。 <Route /> 用来显式地把路由映射到应用的组件结构上。 用 path 指定 URL,用 component 指定路由命中 URL 后需要渲染的那个组件。

实际使用就得按照此写法进行灵活配置,用到项目中我们需要把它给拆分灵活用到各个地方。

先来看下项目的目录结构:


目录结构

首先的配置主入口文件 main.js :

import React from "react";
import ReactDom from "react-dom";
import { createStore } from 'redux';
import { Provider } from 'react-redux';
// 以上均是常规配置项
// Router用来包裹路由route路由路径和对应的组件
// browserHistory,hashHistory路由采用是路径形式还是采用hash#形式
import { Router, Route, browserHistory,hashHistory } from 'react-router';
// syncHistoryWithStore同步路由到store里面,然组件可以访问
// routerReducer是具体的执行者,在combineReducers里面起作用
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';

// 引入纯函数
import reducers from "./reducers";
// 需要使用的组件
import App from "./views/App";
import Xixi from "./views/Xixi";
import Haha from "./views/Haha";
import Son from "./views/Son";
// 创建仓库
const store = createStore(reducers);
 
// 路由同步到仓库里面
const history = syncHistoryWithStore(hashHistory, store);

// Route平级就是同级路由
// 嵌套就是层级路由
ReactDom.render(
    <Provider store={store}>
        <Router history={history}>
            <Route path="/" component={App}>
                <Route path="/haha" component={Haha}></Route>
                <Route path="/son" component={Son}></Route>
            </Route>
            <Route path="/xixi" component={Xixi}></Route>
        </Router>
    </Provider>
,document.querySelector("#app"));

再在 reducer 里面配置一下 routing (routing会把路由的信息放在store中)

import {combineReducers} from "redux";
import reducer from "./reducer";
import { routerReducer } from 'react-router-redux';

export default combineReducers({
    reducer,
    routing: routerReducer
});

这时访问二级路由发现并没有二级路由的页面,关键的在一级目录里面配置这条命令:

import React, { Component } from 'react'

export default class App extends Component {
    render() {
        return (
            <div>
                <h1>我是最大的组件APP</h1>
                {this.props.children}//双标签里面的内容
            </div>
        )
    }
}

配置完毕的运行发现出现警告:

WARNING in ./main.js 11:37-51
"export 'browserHistory' was not found in 'react-router'
i 「wdm」: Compiled with warnings.

react-router 里面找不到 browserHistory。怀疑可能是安装包冲突的原因。
进入 node_modules\react-router-redux 检查 package.json 检查依赖的 react-router 最高支持到 "react-router": "^3.0.0" 而我们安装的是 "react-router": "^5.0.1"版本冲突了,现在卸载重装低版本的。

卸载命令:

npm uninstall react-router

重装指定版本:

npm install --save react-router@3

再次运行即可访问路由。

访问二级路由页面 son 这个页面:
http://127.0.0.1:8080/#/son


son.js 代码:

import React, { Component } from 'react';
import {connect} from "react-redux";

@connect(
    ({routing})=>({
        routing
    })
)
export default class Son extends Component {
    render() {
        return (
            <div>
                <h1>我是son组件</h1>
                {JSON.stringify(this.props.routing)}
            </div>
        )
    }
}
代码优化

把路由给提取出来,作为一个单独文件,因为路由里面要用到 history 而 history 又要用到 store ,store又必须放在首页,所以如果暴露的是组件就得用 props 传参,如果是函数那就用函数传参。这里暴露成函数。

路由文件 router.js

import React from "react";
import { Router, Route } from 'react-router';
// 需要使用的组件
import App from "./views/App";
import Xixi from "./views/Xixi";
import Haha from "./views/Haha";
import Son from "./views/Son";

var router;
export default router = (history)=> <Router history={history}>
    <Route path="/" component={App}>
        <Route path="/haha" component={Haha}></Route>
        <Route path="/son" component={Son}></Route>
    </Route>
    <Route path="/xixi" component={Xixi}></Route>
</Router>

相应的 main.js 就减负变成了:

import React from "react";
import ReactDom from "react-dom";
import { createStore } from 'redux';
import { Provider } from 'react-redux';
// 以上均是常规配置项
// Router用来包裹路由route路由路径和对应的组件
// browserHistory,hashHistory路由采用是路径形式还是采用hash#形式
import { Router, Route, browserHistory,hashHistory } from 'react-router';
// syncHistoryWithStore同步路由到store里面,然组件可以访问
// routerReducer是具体的执行者,在combineReducers里面起作用
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';

// 引入纯函数
import reducers from "./reducers";

// 创建仓库
const store = createStore(reducers);

import router from "./router";
// 路由同步到仓库里面
const history = syncHistoryWithStore(hashHistory, store);

// Route平级就是同级路由
// 嵌套就是层级路由
ReactDom.render(
    <Provider store={store}>
        {router(history)}
    </Provider>
,document.querySelector("#app"));
二、子路由的两种书写方法

在书写子路由的时候,我们常用的方法是两种,第一种就是嵌套法。第二种就是外包法。两种方法各有优劣,看个人喜好,不过原理大致都是相同的。

  • 路由嵌套
<Router history={history}>
    <Route path="/" component={App}>
        <Route path="/haha" component={Haha}></Route>
        <Route path="/son" component={Son}></Route>
    </Route>
    <Route path="/xixi" component={Xixi}></Route>
</Router>

这种很常规,一层套一层的书写下去。

  • 外包法
<Router history={history}>
    <Route path="/" component={App}></Route>
    <Route path="/haha" component={Haha}></Route>
    <Route path="/xixi" component={Xixi}></Route>
</Router>

这种书写就是平行结构的了,这个写法特别像 Promise 解决异步回到黑洞的感觉。
同时要注意的是路由到的文件要想显示父路由的内容的使用父标签来包裹子标签。这就是外包法的本质。就像上面路由到 haha 这个路由,同时我也想显示 / 跟路由对应的 APP 组件。就得这样写:
haha.js

import React, { Component } from 'react'
import App from "./App";
export default class Haha extends Component {
    render() {
        return (
            <App>
                <h1>我是哈哈组件</h1>
            </App>
        )
    }
}

父组件 App.js 有 this.props.children 会把 haha.js 的内容放到 App.js 里面显示。

三、路由跳转

点击按钮或标签能够跳到不同 URL 对应的组件。一共两种方法:

  • Link 方法(属于react-router)
    这里的 Link 其实和 a 标签差不多。
    修改 HaHa.js 组件
import React, { Component } from 'react';
// 首先我们需要Link
import {Link} from 'react-router';
import App from "./App";
export default class Haha extends Component {
    render() {
        return (
            <App>
                <h1>我是哈哈组件</h1>
                {/* 把 <Link> 其实就是 <a> */}
                <Link to = "/">点击回到首页</Link>
            </App>
        )
    }
}

路由到 HaHa 的路由路径变为了:

<Route path="/students/haha" component={Haha}></Route>
Link组件跳转.gif
  • dispatch 方法(react-router-redux)

参考 npm react-router-redux 官网案例如下:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import { routerMiddleware, push } from 'react-router-redux'
 
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
  reducers,
  applyMiddleware(middleware)
)
 
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))

接下来我们需要把它配置到自己的项目上。main.js 配置中间件,自己找个地方放代码:

import { routerMiddleware } from 'react-router-redux';
const middleware = routerMiddleware(hashHistory);
const store = createStore(reducers,applyMiddleware(middleware));

配置完成在我们子组件中就可以引入 push 发送进行跳转了。

import React, { Component } from 'react';
import {connect} from 'react-redux';
import {push} from 'react-router-redux';
import App from "./App";

@connect(
    ({reducer})=>({
        reducer
    }),
    dispatch=>({
        dispatch
    })
)
export default class Haha extends Component {
    render() {
        return (
            <App>
                <h1>我是哈哈组件</h1>
                <button onClick = {()=>{
                    this.props.dispatch(push('/'));
                }}>按我跳转</button>
            </App>
        )
    }
}

至于两个跳转方法用的地方,Link 一般用来替代 a 标签,dispatch 往往使用事件进行控制。自己和酌情使用。

四、重定向(Redirect)跳转

这个东西特别类似服务器的 301 重定向功能。
我们现在使用子组件的 dispatch 功能调到一个假路由:

this.props.dispatch(push('/wu'));

很明显路由到这个地方什么组件也没有,我们给他重定向到首页:

<Redirect from = "/wu" to = "/"></Redirect>

这时就会自动跳到首页了。
from
你想由哪个路径进行重定向,包括动态段。
to
你想重定向的路径。

五、默认路由(IndexRoute)与 IndexLink

在我们不使用默认路由的时候,一个路由配置大约是这样的:

<Router history={history}>
    <Route path="/" components={App}>
        <Route path="haha" component={Haha}></Route>
        <Route path="xixi" component={Xixi}></Route>
    </Route>
</Router>

当用户访问 / 时, App 组件被渲染,但组件内的子元素却没有, App 内部的 this.props.children 为 undefined 。 当然我们可以简单地使用 {this.props.children || } 来规避这个问题。
但现在有另一种方法:使用 indexRoute 来为 this.props.children 赋值。

<Router history={history}>
    <Route path="/" components={App}>
        <IndexRoute component={Home}/>
        <Route path="haha" component={Haha}></Route>
        <Route path="xixi" component={Xixi}></Route>
    </Route>
</Router>

当我们在试图访问首页的时候内容变成了:

indexRoute

如果接着访问 http://127.0.0.1:8080/#/haha,相应的 indexRoute 就不在起作用了。

六、路由路径
  • <Route path="/:id" components={App}> 冒号表示匹配一段位于 /、? 或 # 之后的 URL。 命中的部分将被作为一个参数。
  • <Route path="/user(/use)" components={App}> – () 表示在它内部的内容被认为是可选的
  • <Route path="/*i*" components={App}> ** 之间的内容表示路由路径后面无论输入什么,只需必须包含的这个内容就行了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容