最近使用的 React + webpack 来开发项目,感受确实是爽的飞起,然而总感受仍是少了点什么。
对,是多页面,每次请求页面还要后端路由给你?多不爽啊,试试 react-router ,简直掌控一切的感受,只开放一个页面路由接口,其余全给数据接口就了,能够和后端哥哥说拜拜了。
??????????? 啪啪啪啪啪~html
先贴上官方文档 https://github.com/rackt/react-router/tree/master/docs.
对了这里还有一份中文文档(不过不是很全)http://react-guide.github.io/react-router-cn/.
react-router 是 React 的完整前端路由解决方案,特别在作一个 spa 应用的时候,他能实现 url 和 视图ui 的同步,而且支持后端渲染,异步按需加载等等。
因为 react-router 文档的多变,这里的例子以当前版本 1.0.1 为准。(1.0以前文档每个版本的变更都很大,索多了都是泪)前端
全部支持 React 的浏览器。
倡议!建议你们也劝说身边的人用现代化浏览器,关爱前端开发者。react
npm install react-router@latest
同时,react-router 是基于 history 开发的,这里你须要安装 history。
注意 react-router 当前版本 1.0.1 依赖的是 history 1.13.1 请不要安装最新版。
不要问我为何知道,被坑惨了;有同窗问,那没有办法用 history 的最新版本嘛?毕竟这只是暂缓之计,解决方案仍是有的,那就是等 react-router 做者解决咯 ?。webpack
npm install history@1.13.1
构建工具的话,我依然建议是 webpack , React 和 webpack 是一对好兄弟。git
npm install webpack
webpack的使用方法能够看个人前两篇文章:es6
如何使用webpack—webpack-howto
(求赞!!!)web
// 加载依赖包,这是 es6 的语法(我好啰嗦)。 import React from 'react' import { render } from 'react-dom' // 这里从 react-router 引入了三个组件,先不解释。 import { Router, Route, Link } from 'react-router' const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } }); const Inbox = React.createClass({ render() { return ( <div> Inbox </div> ) } }); const About = React.createClass({ render() { return ( <div> About </div> ) } }); render(( <Router> <Route path="/" component={App}> <Route path="about" component={About} /> <Route path="inbox" component={Inbox} /> </Route> </Router> ), document.getElementById('root'));
我就偷个懒把官方文档的demo直接copy了。
直接先来看 render 下面的内容,这里是用 jsx 语法。 最外层组件是 Router(能够把他看做是react-router提供的最外层容器) , 下一层是 Route,这个是路由组件。对应关系以下:/ ---> <App />
/about ---> <App><About /></App>
/inbox ---> <App><Inbox /></App>
path 对应路径,component 对应当前路径渲染的组件。
Route 里面的 Route 表示在父组件路由的 path 路径下面的一级 path 对应的路由,这里的路由是父子嵌套,对应的组件也一样是父子嵌套的。
若是是多级嵌套也一样如此。算法
以上例子能够改写成:npm
... const routerConfig = [ { path: '/', component: App, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] } ]; render(( <Router routes={routeConfig} /> ), document.getElementById('root'));
这里的结构就更清晰了,我是比较喜欢这种方式。
好比上面的例子,/ 对于的组件是 App , 若是 App 只是渲染了一个导航条,却没有自组件,那打开 好比 qiutc.me/ 的时候不是就没有内容了吗。
把上面例子改一下
... // 添加组件 Index const Index = React.createClass({ render() { return ( <div> Index Index Index </div> ) } }); // 修改配置 const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] } ];
这里加了一个 indexRoute
,表示他在没有匹配子路由的时候,在 / 路由下渲染默认的子组件 Index。
路由组件嵌套也是同样的,好比:
{ path: 'about', component: About, indexRoute: {component: AboutIndex} },
以此类推。
若是咱们打开了一个没有设置路由的连接,就必然须要一个友好的 404 页面。配置以下:
... // 添加 404 组件 const NotFound = React.createClass({ render() { return ( <div> 404 NotFound </div> ) } }); // 修改配置 const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] }, { path: '*', component: NotFound, } ];
如此简单。
const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox, childrenRoutes: [ { path: 'message/:id', component: Message, } ], }, ] }, { path: '*', component: NotFound, } ];
在这里咱们访问 /inbox/message/1 对于渲染 Message 组件,这个连接太长了,咱们想直接 /message/1 那怎么办,改路由结构?太麻烦了!绝对路径能够帮你作到这个。
把 path: 'message/:id',
改成 path: '/message/:id',
就行了。
等等若是用户以前收藏的连接是 /inbox/message/1 ,那不是就打不开了嘛,和后端路由同样,react-router 也有重定向:redirect
const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox, childrenRoutes: [ { path: '/message/:id', component: Message, }, { path: 'message/:id', onEnter: function (nextState, replaceState) { replaceState(null, '/messages/' + nextState.params.id); } } ], }, ] }, { path: '*', component: NotFound, } ];
function (nextState, replaceState) { replaceState(null, '/messages/' + nextState.params.id); }
nextState
表示要进入的下一个路由,这里就是 /inbox/messages/:id ,replaceState
表示替换路由状态的方法,把 /inbox/messages/:id 替换成 /messages/:id,而后就能够重定向到 /messages/:id。
表示在离开路由前执行。
———————————————————
固然若是你用的是 jsx 语法,有更简单的组件能够实现:
import { Redirect } from 'react-router' React.render(( <Router> <Route path="/" component={App}> <IndexRoute component={Index} /> <Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Route path="/messages/:id" component={Message} /> {/* 跳转 /inbox/messages/:id 到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route> </Route> </Router> ), document.getElementById('root'))
React Router 使用路由嵌套的概念来让你定义 view 的嵌套集合,当一个给定的 URL 被调用时,整个集合中(命中的部分)都会被渲染。嵌套路由被描述成一种树形结构。React Router 会深度优先遍历整个理由配置来寻找一个与给定的 URL 相匹配的路由。
简单来说,就是说,匹配的时候会先匹配到外层路径,而后依次遍历到内层。
好比 /inbox/messages/:id 会先匹配 /,渲染 / 对应的组件 App,而后再到 / 的下一层寻找 /inbox ,一样渲染 /inbox 对应的组件 Inbox,依次类推,直到到 message/:id。
tip:使用绝对路径能够忽略嵌套关系,如上面例子。
路由路径是匹配一个(或一部分)URL 的 一个字符串模式。大部分的路由路径均可以直接按照字面量理解,除了如下几个特殊的符号:
/:paramName – 匹配一段位于 /、? 或 # 以后的 URL。 命中的部分将被做为一个参数
(/) – 在它内部的内容被认为是可选的
/* – 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并建立一个 splat 参数
<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
最后,路由算法会根据定义的顺序自顶向下匹配路由。所以,当你拥有两个兄弟路由节点配置时,你必须确认前一个路由不会匹配后一个路由中的路径。例如:
<Route path="/comments" ... /> <Redirect from="/comments" ... />
第二个是不会被执行的。
好比上面的 /messages/:id ,这个id多是咱们在 Message 获取数据时须要的 id。
他会被当作一个参数传给 params,parmas 会传给 Message 组件的 props:
const Message = React.createClass({ render: function() { return ( <div>{ this.props.params.id }</div> ); } });
这样就能够获取到了。
React Router 是创建在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 而后 router 使用它匹配到路由,最后正确地渲染对应的组件。
经常使用的 history 有三种形式, 可是你也可使用 React Router 实现自定义的 history。
createHashHistory
createBrowserHistory
createMemoryHistory
这是一个你会获取到的默认 history ,若是你不指定某个 history 。它用到的是 URL 中的 hash(#)部分去建立形如 example.com/#/some/path 的路由。
这个 支持 ie8+ 的浏览器,可是由于是 hash 值,因此不推荐使用。
Browser history 是由 React Router 建立浏览器应用推荐的 history。它使用 History API 在浏览器中被建立用于处理 URL,新建一个像这样真实的 URL example.com/some/path。
不会在地址栏被操做或读取。
import { createBrowserHistory, useBasename } from 'history'; const historyConfig = useBasename(createHistory)({ basename: '/' // 根目录名 }); ... render(( <Router routes={routeConfig} History={historyConfig} /> ), document.getElementById('root'));
咱们在最开头看到这样一个东西:
const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } });
Link 会被渲染成 a ,to 其实就是 href ,
可是 react-router 会阻止默认跳转页面,而改为 history 路由的变换。
参数:
to
切换到的路由地址
query
跟在 url 的 query 参数,好比
query={{q: "que"}} 对应 `/example?a=que
这里的 query 一样能够像 params 会被传入下一个路由组件的 props
hash
跟在 url 的 hash 参数,好比
hash={111} 对应 `/example#111
这里的 query 一样能够像 params 会被传入下一个路由组件的 props
activeClassName
当前 url 路径若是和 Link 的 to 匹配 这个 Link 就会有一个定义的属性,好比:
在 /index 下 <Link to="/index" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 这里渲染出来的 a 标签会有一个激活的 active 类名,还会有一个颜色 red <Link to="/about" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 这里渲染出来的 a 标签就不会有以上属性
activeStyle
同上
onClick
点击的时候执行的函数,会传入一个 e 事件对象,你能够 e.stopPropagation()
阻止默认路由切换。
在上面有一个问题若是:
在 / 下 和 /index <Link to="/" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 这个 Link 渲染出来的 a 标签都会激活 active 属性,而且会带上 color: 'red' 由于 / 和 /index 和 / 都是匹配的
这时候就能够用:
<IndexLink to="/" activeClassName={"active"} activeStyle={{color: 'red'}} >/</IndexLink> 只会在 / 下呗激活,在 /index 或者其余下面,不会被激活
关于 根据路由按需异步加载js 和 服务器端渲染路由视图 以及 react-router的更高级用法 会在下一篇文章来探讨。毕竟哥也须要去深刻研究一下才敢献丑。?