React router

理解 react-router

react 的一个插件库
专门用来实现一个 SPA 应用
基于 react 的项目基本都会用到此库

几个重要问题

SPA 应用

单页 Web 应用(single page web application,SPA)
整个应用只有一个完整的页面
点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
当点击链接时, 只会做页面的局部更新
数据都需要通过 ajax 请求获取, 并在前端异步展现

路由

  1. 什么是路由?
    一个路由就是一个映射关系(key:value)
    key 为路由路径, value 可能是 function/component
  2. 路由分类
    后台路由: node 服务器端路由, value 是 function, 用来处理客户端提交的请求并返回一个响应数据
    前台路由: 浏览器端路由, value 是 component, 当请求的是路由 path 时, 浏览器端前没有发送 http 请求, 但界面会更新显示对应的组件
  3. 后台路由
  • 注册路由: router.get(path, function(req, res))
  • 当 node 接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
  • 前端路由
    • 注册路由:
    • 当浏览器的 hash 变为#about 时, 当前路由组件就会变为 About 组件

关于 url 中的#

  1. 理解#
    ‘#’代表网页中的一个位置。其右面的字符,就是该位置的标识符
    改变#不触发网页重载
    改变#会改变浏览器的访问历史
  2. 操作#
    window.location.hash 读取#值
    window.onhashchange = func 监听 hash 改变
  3. 学习资源:
    阮一峰教程: http://www.ruanyifeng.com/blog/2011/03/url_hash.html

react-router 的学习资源

github 主页: https://github.com/ReactTraining/react-router
官网教程: https://github.com/reactjs/react-router-tutorial
阮一峰教程: http://www.ruanyifeng.com/blog/2016/05/react_router.html

相关 API

react-router 中的相关组件:

  • Router: 路由器组件, 用来包含各个路由组件
  • Route: 路由组件, 注册路由
  • IndexRoute: 默认子路由组件
  • hashHistory: 路由的切换由 URL 的 hash 变化决定,即 URL 的#部分发生变化
  • Link: 路由链接组件

Router: 路由器组件

属性: history={hashHistory} 用来监听浏览器地址栏的变化, 并将 URL 解析成一个地址对象,供 React Router 匹配
子组件: Route

Route: 路由组件

属性 1: path=”/xxx”
属性 2: component={Xxx}
根路由组件: path=”/“的组件, 一般为 App
子路由组件: 子配置的组件

IndexRoute: 默认路由

当父路由被请求时, 默认就会请求此路由组件

hashHistory

用于 Router 组件的 history 属性
作用: 为地址 url 生成?_k=hash, 用于内部保存对应的 state

属性 1: to=”/xxx”
属性 2: activeClassName=”active”

react-router 的基本使用

下载

1
npm install react-router --save

定义各个路由组件

  1. About.js
1
2
3
4
5
import React from "react";
function About() {
return <div>About 组件内容</div>;
}
export default About;
  1. Home.js
1
2
3
4
5
import React from "react";
function Home() {
return <div>Home 组件内容 2</div>;
}
export default Home;
  1. Repos.js
1
2
3
4
5
6
import React, { Component } from "react";
export default class Repos extends Component {
render() {
return <div>Repos 组件</div>;
}
}
  1. App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from "react";
import { Link } from "react-router";

export default class App extends Component {
render() {
return (
<div>
<h2>Hello, React Router!</h2>
<ul>
<li>
<Link to="/about" activeClassName="active">
About2
</Link>
</li>
<li>
<Link to="/repos" activeClassName="active">
Repos2
</Link>
</li>
</ul>
{this.props.children}
</div>
);
}
}

index.js: 注册路由, 渲染路由器标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from "react";
import { render } from "react-dom";
import { Router, Route, IndexRoute, hashHistory } from "react-router";
import App from "./modules/App";
import About from "./modules/About";
import Repos from "./modules/Repos";
import Home from "./modules/Home";

render(
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/about" component={About}></Route>
<Route path="/repos" component={Repos}></Route>
</Route>
</Router>,
document.getElementById("app")
);

主页面: index.html

1
2
3
.active {
color: red;
}

向路由组件传递请求参数

repo.js: repos 组件下的分路由组件

1
2
3
4
5
6
7
8
9
import React from "react";
export default function ({ params }) {
let { username, repoName } = params;
return (
<div>
用户名:{username}, 仓库名:{repoName}
</div>
);
}

repos.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import React from "react";
import NavLink from "./NavLink";

export default class Repos extends React.Component {
constructor(props) {
super(props);
this.state = {
repos: [
{ username: "faceback", repoName: "react" },
{ username: "faceback", repoName: "react-router" },
{ username: "Angular", repoName: "angular" },
{ username: "Angular", repoName: "angular-cli" }
]
};
this.handleSubmit = this.handleSubmit.bind(this);
}

handleSubmit() {
const repos = this.state.repos;
repos.push({
username: this.refs.username.value,
repoName: this.refs.repoName.value
});
this.setState({ repos });
this.refs.username.value = "";
this.refs.repoName.value = "";
}

render() {
return (
<div>
<h2>Repos</h2>
<ul>
{this.state.repos.map((repo, index) => {
const to = `/repos/${repo.username}/${repo.repoName}`;
return (
<li key={index}>
<Link to={to} activeClassName="active">
{repo.repoName}
</Link>
</li>
);
})}
<li>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="用户名" ref="username" /> /{" "}
<input type="text" placeholder="仓库名" ref="repoName" />{" "}
<button type="submit">添加</button>
</form>
</li>
</ul>
{this.props.children}
</div>
);
}
}

index.js: 配置路由

1
2
3
<Route path="/repos" component={Repos}>
<Route path="/repos/:username/:repoName" component={Repo} />
</Route>

优化 Link 组件

1
2
3
4
5
import React from "react";
import { Link } from "react-router";
export default function NavLink(props) {
return <Link {...props} activeClassName="active" />;
}

Repos.js

1
<NavLink to={to}>{repo.repoName}</NavLink>