一、路由使用起步配置
安装两个依赖:
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>
- 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>
当我们在试图访问首页的时候内容变成了:
如果接着访问 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}> **
之间的内容表示路由路径后面无论输入什么,只需必须包含的这个内容就行了。