Null's Blog

React版博客系列3--路由系统

系列链接:

1.React版博客系列1–基础说明与标签组件
2.React版博客系列2–分页组件设计
3.React版博客系列3–路由系统

先上demo

本系列源码: blog_react
数据服务: blog_api
线上预览: https://react.nullcn.com

接上篇 react-router简介

现在我们引入 react-router 。用过类似 router ,比如 MVVM 框架 angular-router 等 router 的,都已经大致了解 router 会提供哪些相应的功能。
总的来说, react-router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。

嵌套关系

react-router 使用路由嵌套的概念来让你定义view的嵌套集合,当一个给定的URL被调用时,整个集合中(命中的部分)都会被渲染。嵌套路由被描述成一种树形结构。 react-router 会深度优先遍历整个理由配置来寻找一个与给定的 URL 相匹配的路由。

路径语法

1
2
3
4
5
6
7
8
9
10
//匹配 /tag/javascript 和 /tag/nodejs
<Route path="/tag/:tag" component={ArticleList} />
//匹配 /tag/hello.jpg 和 /tag/path/to/hello.jpg
<Route path="/tag/*.*" />
//匹配 /tag, /tag/javascript 和 /tag/nodejs
<Route path="/tag(/:tag)" component={ArticleList} />
<Route path="tag" component={ArticleList}>
//匹配 /tag/javascript/page/1
<Route path="/:tag/page/:pageIndex" component={ArticleList} />
</Route>

优先级

1
2
3
4
//先定义 先匹配 没有路由传递
<Route path="/tag/:tag" component={ArticleList} to={{queryType:'tag'}}/>
//如果这行在前面,就不会 匹配到 /tag/javascript这种了 因为tag这个确定的单词被当作:year了
<Route path="/:year/:month" component={ArticleList} to={{queryType:'archive'}}/>

关于History

本博客暂不打算使用 History,而是使用 nginx 配置 urlwrite ,之后由 react-router 自身去获取 url 实现跳转

react-router本博客形态(SPA)的基石,可以想象成整个工程的构建骨架,数据流为:

1
2
3
4
5
6
/->react-router->ArticleList //默认第1页
page.pageindex->react-router->ArticleList //默认第pageindex页
tag->react-router->ArticleList //tagName 默认第1页
tag.pageindex->react-router->ArticleList //tagName 默认第pageindex页
archive->react-router->ArticleList //archive 默认第1页
archive.pageindex->react-router->ArticleList //archive 默认第pageindex页

参数传递

1
2
3
4
//文章列表组件根据queryType 查询tag列表
<Route path="/tag/:tag" component={ArticleList} to={{queryType:'tag'}}/>
//文章列表组件根据queryType 查询archive列表
<Route path="/:year/:month" component={ArticleList} to={{queryType:'archive'}}/>

具体配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ~/index.js
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={ArticleList} to={{queryType:'/'}}/>
<Route path="/page/:pageIndex" component={ArticleList} to={{queryType:'/'}}/>
<Route path="/tag/:tag" component={ArticleList} to={{queryType:'tag'}}/>
<Route path="/tag/:tag/" component={ArticleList} to={{queryType:'tag'}}/>
<Route path="/tag/:tag/page/:pageIndex" component={ArticleList} to={{queryType:'tag'}}/>
<Route path="/tag/:tag/page/:pageIndex/" component={ArticleList} to={{queryType:'tag'}}/>
<Route path="/:year/:month" component={ArticleList} to={{queryType:'archive'}}/>
<Route path="/:year/:month/" component={ArticleList} to={{queryType:'archive'}}/>
<Route path="/:year/:month/page/:pageIndex" component={ArticleList} to={{queryType:'archive'}}/>
<Route path="/:year/:month/page/:pageIndex/" component={ArticleList} to={{queryType:'archive'}}/>
<Route path="/:year/:month/:name" component={Article} to={{queryType:'article'}} />
</Route>
</Router>
),document.getElementById('root'));

#web容器urlwrite配置 这里以nginx为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name react.nullcn.com;
location / {
root /var/react;
try_files $uri uri/ /index.html;
}
location ~ (.+)\.(html|json|txt|js|css|jpg|jpeg|gif|png|svg|ico|eot|otf|woff|woff2|ttf)$ {
try_files $uri @rootfiles;
}
location @rootfiles {
root /var/react;
rewrite ^/(.*)/(.*) /$1 redirect;
}
}

通过 urlrewrite 将所有该二级域名请求的所有资源文件,路径等重写到index.html,将所有的请求统一交给 react-router 处理实际效果:
实际效果

tag组件与archive组件调整

看了实际效果或者用过类 router 组件之后,可以很清楚的看到,页面并没有跳转,所以肯定不会使用 <a> 标签,而是使用 react-router 自带的 <Link>标签. <Link> 在 react-router 内部负责通知 react-router 需要跳转的路由,再由指定的component渲染相应的组件.
使用标签与使用其他 jsx 标签一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ~/assets/blog/js/TagList.js
render(){
if(this.state.loading){
return <span>Loading...</span>
}else if(this.state.error != null){
return <span>Error:{this.state.error.message}</span>
}else{
var tags = this.state.data;
var taglist = tags.map(function (tag) {
return (
<li className="tag-list-item" key={tag.tag}>
//<a className="tag-list-link" href={"/tag/"+tag.tag+"/"}>{tag.tagName}</a> 原先是a标签
<Link className="tag-list-link" to={"/tag"+"/"+tag.tag+"/"}>{tag.tagName}</Link>
</li>
);
});
return (
<ul className="tag-list">
{taglist}
</ul>
);
}
}

这里就不再介绍archive组件组件的调整了,大同小异.

下篇预告

Article组件与ArticleList组件:
根据不同的参数请求不同的接口(标签或归档)的数据,组织文章列表结构,渲染博客的主题部分(文章列表与分页),通知分页控件等.