路由可以向应用中快速的添加视图和数据流,同时保持页面与URL同步
import {Router ,Route, Link} from 'react-router' class App extends React.Component { render(){ return ( <div> <ul > <li><Link to='/about'>About</Link></li> <li><Link to='/inbox'>Inbox</Link></li> </ul> {this.props.children} </div> )}})
//获取URL参数
class Message extends React.Component{
componentDidMount(){
const id = this.props.params.id //来自于路径 ‘/inbox/messages/:id'
fetchMessage(id,function(err,message){
this.setState({message:message})
})
}
render(){
return(
<div>
<h3>message{this.props.params.id}</h3>
</div>
}
React.render(( <Router> <Route path='/' component={App}> <Route path='about' component={About} /> <Route path='inbox' component={Inbox} onEnter={requireAuth}>
<Route path='messages/:id component={Message} />
</Route>
</Route> <Router> ).document.body);
以上获取URL参数方法:
1 id = this.props.params.id 来自父组件路径path=‘/index/message.:id'
2 baz= this.props.location.query.bar 访问路径为 /foo?bar=baz 时,获取baz的值
3 newhash = window.location.hash 简单的用于跳转,不用存储location state
添加首页:IndexRouter 设置默认页面,当App的render中this.props.children还是undefined时显示
<IndexRoute component={Dashboard}/>
IndexLink
如果使用<Link to='/'>Home</Link>,他会一直处于激活状态,因为所有的URL开头都是‘/'。而我们希望仅仅在home被渲染之后才激活并链接它。 可以使用<IndexLink to='/'>home</IndexLink>.
简洁的URL:使用绝对路径,无需在URL中添加更多的层级,从而可以使用更简洁
<Route path='/messages/:id' component={Message}/> 但其可能在动态路由中无法使用
重定向Redirect
<Redirect from='message/:id' to='/message/:id'/>
当有人点击/inbox/message/5时,会跳到/message/5,以防/inbox/message/5不存在
onEnter和onLeave进入和离开的hook,在页面跳转确认时触发。例如 权限验证,或数据持久保存
onLeave hook 在所有将离开的路由中触发,从最下层的字路由开始到最外层的父路由结束。
onEnter hook从最外层的父路由开始到最下层的字路由结束
例如:从/messages/5跳到/about,hook的执行顺序:/messages/:id的onLeave->/inbox的onLeave->/about的onEnter
requireAuth(nextState,replace,callback){if(isLogined){replace('./home');}else{replace('./login');}callback();}
路径语法
:paramName 匹配位于/ ? # 之后的URL;称作参数
() 在其内部的内容是可选的
* 匹配任意字符
History
一个history知道如何去监听浏览器地址栏的变化,并解析这个url转化为location对象,然后router使用它匹配到路由,渲染对应的组件。
1 browserHistory
使用浏览器中的 historyAPI 用于处理URL,创建 example.com/some/path这样的真实URL
2 hashHistory
我使用URL中的hash#部分去创建形如 example.com/#/some/path的路由.window.location.hash = newhash
3 creactMemoryHistory
d Memory history 不会在地址栏被操作或读取,非常适合测试和其他渲染环境。但必须创建
const history = createMemoryHistory(location)
高级进阶
动态路由:
根据‘按需加载’的原则,路由非常适合做代码拆分:他的责任就是配置好每个view。
react router 的 路径匹配 及 组件加载 都是异步完成的,不仅允许你延迟加载组件,也可以延迟加载 路由的配置。在首次加载包中只需要有一个路径定义,路由会自动解析剩下的路径。
route定义的函数 getChildRoutes、getIndexRoute、getComponents 都为异步加载‘逐渐匹配’
const CourseRoute = { path:'cource/:id', getChildRoutes(location,callback){ require.ensure([],function(require){ callback(null,[ require('./routes/Announcements'), require('./routes/Assignments'), require('./routes/Grades'), ])})}, getIndexRoute(location,callback){ require.ensure([],function(require){ callback(null,require('./components/Index')) })}} getComponents(location,callback){ require.ensure([],function(require){ callback(null,require('./components/Course'))})}}
跳转前确认:React Route 提供一个routerWillLeave的生命周期钩子,使得react组件可以拦截正在发生的跳转,或在离开route前提示用户。
routerWillLeave 返回值有以下两种:
1 return false 取消此次跳转
2 return 返回提示信息,在离开route前提示用户进行确认。
使用:1在route组件中引入 lifecycle mixin来安装这个钩子
import { Lifecycle } from 'react-router' const Home = React.creactClass({ //假设Home是一个 route 组件,使用 Lifecycle mixin 去获取一个 routerWillLeave 方法。 mixins:[Lifecycle], routerWillLeave(nextLocation){ if(!this.state.isSaved) return 'Your work is not saved! Are you sure leave?' }, ..... })
2 如果想在一个深层次嵌套的组件中使用 routerWillLeave 钩子,只需在route组件中引入RouteContext mixin,这样就会把route放到context中。
import { Lifecycle, RouteContext } from 'react-router' const Home = React.creactClass ({ //route 会被放在Home 和它子组件及孙子组件的context中,这样在层级树中Home及其所有子组件都可以拿到 route。 mixin: [ RouteContext ], render(){ return <NestedForm /> } }) const NestedForm = React.creactClass({ //后代组件使用Lifecycle mixin 获得 一个routerWillLeave 的方法。 mixins:[Lifecycle], routerWillLeave(nextLocation){ if(!this.state.isSave) return 'your work is not saved !' }, ...... })
服务端渲染:
需求:放生错误时返回500, 需要重定向时返回30x,在渲染之前获得数据router
方法:在<Router>API下一层使用:
1 使用 match 在渲染之前根据 location 匹配route
2 使用 RoutingContext 同步渲染 route 组件
import { renderToString } from 'react-dom/server' import { match,RoutingContext } from 'react-router' import routes from './routes' serve((req,res) => { //这里的req.url应该是从初识请求中获得的 //完整的URL路径,包括查询字符串 match({routes,location:req.url},{error,redirectLocation,renderProps) => { if(error){ res.send(500,error.message) }else if (redirectLocation){ res.redirect(302,redirectLocation.pathname + redirectLocation.search }else if (renderProps){ res.send(200,renderToString(<RoutingContext {...renderProps} //renderProps可以构建任何想要的形势 }else{ res.send(404,'Not found') }})})
路由组件的生命周期中获取数据:最简单的方法是通过router组件的生命周期Hook来实现。
理解当路由改变时组件生命周期的变化,可以在Invoice组件里实现一个简单的数据获取功能
let Invoice = React.creactClass({ getInitialState(){ return { invoice:null } }, componentDidMount(){ //初始化数据 this.fetchInvoice() }, componentDidUpdate(prevProps){ //通过参数更新数据 let oldId = prevProps.params.invoiceId let newId = this.props.params.invoiceId if (newId !== oldId) this.fetchInvoice() }, componentWillUnmount(){ //在组件移除前 忽略正在进行中的请求 this.ignoreLastFetch = true }, fetchInvoice(){ let url = '/api/invoices/${this.props.params.invoiceId} this.request = fetch(url,(err,data) => { if (!this.ignoreLastFetch) this.setState({invoice:data.invoice}) }) }, render(){ return <InvoiceView invoice = {this.state.invoice}/> } })
组件外部跳转:
在组件内部使用this.context.router来实现导航;
在组件外部使用Router组件上被赋予的history可以实现导航。
//main file 里renders a Router import { Router,browserHistory } from 'react-router' import routes from './app/routes' render(<Router history={browserHistory} routers={routes} />.el) // a redux/flux action file: import { browserHistory } from 'react-router' browserHistory.push('/some/path')
浏览器的前进后退按钮发生了什么????
web浏览器的历史记录相当于一个仅有入栈的栈,当用户浏览器到某一个页面时将该文档存入到栈中,点击「后退」或「前进」按钮时移动指针到 history 栈中对应的某一个文档。
- 使用
hashchange
事件来监听window.location.hash
的变化 - hash 发生变化浏览器会更新 URL,并且在 history 栈中产生一条记录
- 路由系统会将所有的路由信息都保存到
location.hash
中 - 在 react-router 内部注册了
window.addEventListener('hashchange', listener, false)
事件监听器 - listener 内部可以通过 hash fragment 获取到当前 URL 对应的 location 对象
在react中监听路由hash改变,包括 浏览器前进回退按钮 :componentWillReceiveProps(nextProps){if(nextProps.location.pathname != this.props.location.pathname){window.addEventListener('hashchange',listener,false)};
点击Link后 路由系统发生????
它的 to、query、hash 属性会被组合在一起并渲染为 href 属性。然后调用history.pushState(state,path) ,
然后调用 window.location.hash
或者 window.history.pushState()
修改了应用的 URL