探索React----第一章:ReactRouterV2基础

来源:互联网 发布:手机直播点歌软件 编辑:程序博客网 时间:2024/06/05 11:52

ReactRouter(V2)基础

1.路由

路由就是Router,这里的路由并不是我们在计算机网路中所学的路由的概念,而是前端的‘路由’,简单的来说就是通过URL的变化,使用路由来跳转到相对应的路径来显示不一样的页面,虽然只是这么一个小功能,但是它却是web应用中一个非常非常非常重要的一步。一个只会写页面的前端工程师不是工程师,顶多就是个工人,工程师的目标就是去架构,去优化,去理解,去运用。所以ReactRouter是必须要迈过的一个坎,为了方便写文章,ReactRouter的知识得再从头学一遍。

2.路由配置

不使用ReactRouter

import React from 'react'import { render } from 'react-dom'//这是创建React组件的ES5的写法,ES6的写法是export default class extends React.component{}//可以看到我们一共创建了4个页面组件,const About = React.createClass({/*...*/})const Inbox = React.createClass({/*...*/})const Home = React.createClass({/*...*/})//当前渲染的是App页面const App = React.createClass({  //初始化路由的状态为'#'后面的字符串  getInitialState() {    return {      route: window.location.hash.substr(1)    }  },  //在页面第一次渲染后随时监听URL上'#'后面的字符串来控制跳转  componentDidMount() {    window.addEventListener('hashchange', () => {      this.setState({        route: window.location.hash.substr(1)      })    })  },  //一旦state重新赋值后会触发当前页面重新渲染,这个时候APP页面的子组件会发生改变,改变的页面自然就是路由的值决定的  render() {    let Child    switch (this.state.route) {      case '/about': Child = About; break;      case '/inbox': Child = Inbox; break;      default:      Child = Home;    }    return (      <div>        <h1>App</h1>        <ul>          <li><a href="#/about">About</a></li>          <li><a href="#/inbox">Inbox</a></li>        </ul>        <Child/>      </div>    )  }})React.render(<App />, document.body)

根据上面的方法,如果在About页面中还有其他子页面,那么case的状况就会变多,并且这个嵌套的结构也会变得更加的复杂。我们再来看一下ReactRouter解决这种路径定向的方式:

使用ReactRouter

import React from 'react'import { render } from 'react-dom'// 首先我们需要导入一些组件...import { Router, Route, Link } from 'react-router'// 然后我们从应用中删除一堆代码和// 增加一些 <Link> 元素...const App = React.createClass({  render() {    return (      <div>        <h1>App</h1>        {/* 把 <a> 变成 <Link> */}        <ul>          <li><Link to="/about">About</Link></li>          <li><Link to="/inbox">Inbox</Link></li>        </ul>        //这里我们注意一下,this.props.children指的该组件的所有子节点        {/*          接着用 `this.props.children` 替换 `<Child>`          router 会帮我们找到这个 children        */}        {this.props.children}      </div>    )  }})// 最后,我们用一些 <Route> 来渲染 <Router>。// 这些就是路由提供的我们想要的东西。React.render((  <Router>    <Route path="/" component={App}>      <Route path="about" component={About} />      <Route path="inbox" component={Inbox} />    </Route>  </Router>), document.body)

这里面Router组件是个容器,里面的Route组件才是关键,Route组件包含path和component两个参数,path是路径,component是我们要渲染的页面,所有的Route组件组成了一个树形结构,Router会先加载外层的Route组件再加载内层的Router组件。所以当我们的路径是/about,最后渲染出的结构就是:

<App>  <about /></App>

这个时候,如果about页面如果还有子页面,我们就可以把子组件包裹在about页面中:

React.render((  <Router>    <Route path="/" component={App}>      <Route path="about" component={About}>          <Route path="messages/:id" component={Message} />      </ Route>      <Route path="inbox" component={Inbox} />    </Route>  </Router>), document.body)

现在访问 URL about/messages/Jkei3c32 将会匹配到一个新的路由,并且它成功指向了 App -> About -> Message 这个 UI 的分支。
仔细观察一下message组件,它后面还跟了一个/:id的字样,这就是我们这个页面接受某个id,就像我们访问CDN的个人博客,就需要带一个个人独有的id,没毛病,这就是URL中的参数。
为了从服务器获取 message 数据,我们首先需要知道它的信息。当渲染组件时,React Router 会自动向 Route 组件中注入一些有用的信息,尤其是路径中动态部分的参数。

const Message = React.createClass({  componentDidMount() {    // 来自于路径 `/about/messages/:id`    const id = this.props.params.id    fetchMessage(id, function (err, message) {      this.setState({ message: message })    })  },  // ...})

3.路由匹配

路由匹配原理

Router会根据URL路径按照深度优先遍历自上而下一层层找相应组件,如果没有那些乱七八糟的通配符,路由的匹配相当简单,顺着路径在对应的组件下面找符合路径的子组件,这个真的没啥好说的。

通配符

通配符匹配规则

(1) :paramName
:paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。
(2) ()
()表示URL的这个部分是可选的。
(3) *
*匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。
(4) **
** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。

通配符匹配实例

<Route path="/hello/:name">// 匹配 /hello/michael// 匹配 /hello/ryan<Route path="/hello(/:name)">// 匹配 /hello// 匹配 /hello/michael// 匹配 /hello/ryan<Route path="/files/*.*">// 匹配 /files/hello.jpg// 匹配 /files/hello.html<Route path="/files/*">// 匹配 /files/ // 匹配 /files/a// 匹配 /files/a/b<Route path="/**/*.jpg">// 匹配 /files/hello.jpg// 匹配 /files/path/to/file.jpg

注意优先级

路由匹配规则是从上到下执行,一旦发现匹配,就不再其余的规则了。

//先匹配第一个,第二个就不会匹配了<Route path="/comments" ... /><Route path="/comments" ... />//同样,为了防止以下这种情况,我们一般把带参数的放在底部<Router>  <Route path="/:userName/:id" component={UserPage}/>  <Route path="/about/me" component={About}/></Router>

4.history

对于history这里的解释我一直很困惑:React Router 是建立在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。
前面的实例命名没有写明history,不还是能根据url进行跳转吗?其实Router组件在不注明history参数时,默认是history={hashHistory},在reactRouter中共有3种history:

hashHistory

import { hashHistory } from 'react-router'render(  <Router history={hashHistory} routes={routes} />,  document.getElementById('app'))

当然你也可以不配置,hashHistory的作用就是监听URL中#后的值的变化,就像前面的例子,对应的URL应该是这种形式: ‘http://example.com/#/some/path‘。同时history会把历史网页push进内存,这样我们就可以实现回退,跳转等操作。

browserHistory

import { browserHistory } from 'react-router'render(  <Router history={browserHistory} routes={routes} />,  document.getElementById('app'))

如果设为browserHistory,浏览器的路由就不再通过Hash完成了,而显示正常的路径’example.com/some/path’,背后调用的是浏览器的History API。所以我们建议的history应该是browerHistory。

createMemoryHistory

Memory history 不会在地址栏被操作或读取。这就解释了我们是如何实现服务器渲染的。同时它也非常适合测试和其他的渲染环境(像 React Native )。

const history = createMemoryHistory(location)

关于history,ReactRouterV4已经有了非常大的改动,这个我们会在V4版本学习笔记中在在探讨。

看最上面的实例我们会发现我们似乎漏掉了一个Home页面,Home页面就是我们在URL中只有’/’加载的App下的默认组件,那么我们如何使Home显示呢?
{this.props.children || }这种写法实现的就是在this.props.children是undefined的情况下(即’/’)加载Home组件,但是因为Home和About,Inbox是同级组件,所以这种结构并不太好。

IndexRoute

IndexRoute就是解决这个问题,显式指定Home是根路由的子组件,即指定默认情况下加载的子组件。

<Router>  <Route path="/" component={App}>    <IndexRoute component={Home}/>    <Route path="accounts" component={Accounts}/>    <Route path="statements" component={Statements}/>  </Route></Router>

现在,用户访问/的时候,加载的组件结构如下:

<App>  <Home/></App>

Link的作用类似于a标签,生成一个链接,用户点击跳转。

<ul>  <li><Link to="/about">About</Link></li>  <li><Link to="/inbox">Inbox</Link></li></ul>

样式:

<Link to="/about" activeStyle={{color: 'red'}}>About</Link><Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>

或者

<Link to="/about" activeClassName="active">About</Link><Link to="/repos" activeClassName="active">Repos</Link>

在Router组件之外,导航到路由页面,可以使用浏览器的History API,像下面这样写。

import { browserHistory } from 'react-router';browserHistory.push('/some/path');

IndexLink是我们在链接到根路由/时使用的组件,为什么要使用IndexLink,因为对于根路由,我们设置样式是就会总匹配,所以我们就需要一个只匹配根路由样式的组件。

<IndexLink to="/" activeClassName="active">  Home</IndexLink>

6.小结

本来是想一口气把ReactRouterV2的知识都学完的,包括之后的ReactRouter高级用法,但是我看了一下高级用法,虽然每小节内容不多,但是却需要花些功夫去理解实践,因此,本篇的内容写到这里,再写也显得冗长。想看更多的直接去看ReactRouter中文文档。
另外,ReactRouterV4早就更新了,里面有非常多的改动,因此,写完ReactRouterV2之后,我们会再去研究ReactRouterV4的内容。

原创粉丝点击