react-router 4.0 初学

React Router 4.0 (以下简称 RR4) 已经正式发布,它遵循React的设计理念,即万物皆组件。所以 RR4 只是一堆 提供了导航功能的组件(还有若干对象和方法),具有声明式(引入即用),可组合性的特点。

为什么要使用router?

使用的目的是在具有单页面结构的优势下,也能通过不同路径匹配不同页面,实现多页面的效果。也就是说一个<route>就是一个单独的页面。

本文档内使用的是 react-router 4.2

安装

只需安装react-router-dom

npm install react-router-dom --save-dev

组件

总的样例代码:
import { BrowserRouter, Route } from 'react-router-dom'

const PrimaryLayout = () => (
  <div className="primary-layout">
    <header>
      Our React Router 4 App
    </header>
    <main>
      <Route path="/" exact component={HomePage} />
      <Route path="/users" component={UsersPage} />
    </main>
  </div>
)

const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>

const App = () => (
  <BrowserRouter>
    <PrimaryLayout />
  </BrowserRouter>
)

render(<App />, document.getElementById('root'))
<BrowserRouter>

由于我们的应用程序是用于浏览器的,所以我们需要将最外层组件(例子中的App)封装在来自 v4BrowserRouter 中。

<Route>

他的作用就是当url中的地中与组件上的path相匹配的时候,渲染该组件内的UI界面。

与之前版本不同的是,之前使用{props.children}来嵌套组件,现在直接使用<Route>,而且<Route>在哪里编写,如果路由匹配,子组件就在哪里渲染。

该组件自带3个render method属性:
1. component:后面跟组件名称,即当路径匹配的时候渲染该component组件;
2. render:后面跟一个函数,进行内联渲染,即当路径匹配时,执行该函数并渲染其中的UI;
3. children:后面跟函数,用的较少。

path属性

path属性规定了什么时候渲染该route组件中的组件。

exact属性

规定了当路由完全匹配path中的路由时,渲染相应组件。

<Switch>

之前的路由匹配都是排他性匹配,意味着一次只能匹配到一条路由;V4的路由匹配时包容性匹配,也就是说,多个<Route>可以同时匹配和渲染。

例子中,我们试图根据路径渲染 HomePage 或者 sersPage。如果从示例中删除了 exact 属性,那么在浏览器中访问 /users 时,HomePageUsersPage 组件将同时被渲染。

在V4中实现排他性匹配非常简单,只要使用<Switch>组件就可以了。使用后,<Switch>内部的各子组件一次只能匹配到一个路由。

如果各个子组件中均没有exact属性,则匹配哪个子组件与其位置是有关的。例如下面这个例子:

<Switch>
    <Route path="/" exact component={HomePage} />
    <Route path="/users/add" component={UserAddPage} />
    <Route path="/users" component={UsersPage} />
    <Redirect to="/" />
</Switch>

我们在 /users 之前策略性地放置了 /users/add 的路由,以确保正确匹配。由于路径 /users/add 将匹配 /users/users/add,所以最好先把 /users/add 放在前面。

当然,如果我们以某种方式使用 exact,我们可以把它们放在任何顺序上。

<Redirect>

这个组件就相当于一个跳转,将当前地址导航到一个新的地址。在例子中的<Switch>中只有在其他路由不匹配的情况下,才会渲染重定向组件。

作用就相当于一个a标签,将页面跳转至指定链接。
例如:

<Link to="/courses" />

除了直接写字符串,to后接一个对象还能携带参数跳转到指定路径:

<Link to={{
  pathname: '/course',
  search: '?sort=name',
  state: { price: 18 }
}} />

这是 <Link>的特殊版,顾名思义这就是为页面导航准备的。因为导航需要有 “激活状态”。


路由的嵌套

路由嵌套实现的方式有很多种,比较简单的就是把子路由和其父路由并排写在一起,如下所示:

<main>
    <Switch>
        <Route path="/" exact component={HomePage} />
        <Route path="/users" exact component={BrowseUsersPage} />
        <Route path="/users/:userId" component={UserProfilePage} />
        <Route path="/products" exact component={BrowseProductsPage} />
        <Route path="/products/:productId" component={ProductProfilePage} />
        <Redirect to="/" />
    </Switch>
</main>

上面的这种写法不是很好,首先父路由的<Route>必须要加上exact属性才能进行匹配渲染相应的组件;其次,子组件的的布局应该是一致的(比如userId为1和2的组件布局是一样的),其中相同的内容会在子组件每一次生成的时候重新请求、渲染,造成了代码的重复,也会使网络流量造成不必要的浪费。

还有一种比较好的写法是将子组件单拿出来,父组件包容性匹配路径,再到子组件中在具体匹配,代码如下所示:

<main>
    <Switch>
        <Route path="/" exact component={HomePage} />
        <Route path="/users" component={UserSubLayout} />        //没有exact属性
        <Route path="/products" component={ProductSubLayout} />     //没有exact属性
        <Redirect to="/" />
    </Switch>
</main>

//嵌套组件
const UserSubLayout = () => (
  <div className="user-sub-layout">
    <aside>
      <UserNav />
    </aside>
    <div className="primary-content">
      <Switch>
        <Route path="/users" exact component={BrowseUsersPage} />
        <Route path="/users/:userId" component={UserProfilePage} />
      </Switch>
    </div>
  </div>
)

在上面代码中,父级路由写在了一起,路径中没有exact属性,做包容性匹配,而相应的子路由被单独拿了出来,写在了父级路由将要渲染的组件里,进行再次的路由匹配。

有一点需要注意的是,即使我们在布局结构中深入嵌套,路由仍然需要识别它们的完整路径才能匹配。为了节省重复输入,改用 props.match.path来代替path中的字符串路径:

const UserSubLayout = props => (
  <div className="user-sub-layout">
    <aside>
      <UserNav />
    </aside>
    <div className="primary-content">
      <Switch>
        <Route path={props.match.path} exact component={BrowseUsersPage} />
        <Route path={`${props.match.path}/:userId`} component={UserProfilePage} />
      </Switch>
    </div>
  </div>
)

完整代码

import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter,HashRouter, Route, NavLink, Switch, Link} from 'react-router-dom';

const PageLayout = ()=> (
    <div>
        <HeaderLayout />
        <div>
            <Route path='/' exact component={Home} />
            <Route path='/schedule' component={Schedule} />
            <Route path='/player' component={Player} />
        </div>
    </div>
)

const HeaderLayout = () => (
    <div className='header'>
        <NavLink to='/' exact activeClassName="active" className>首页</NavLink>
        <NavLink to='/schedule' activeClassName="active" className>赛程</NavLink>
        <NavLink to='/player' activeClassName="active" className>球员表</NavLink>
    </div>
)

const Home =() => (
    <h1>这里是首页</h1>
)

const Schedule = () => (
    <ul>
        <li>one</li>
        <li>two</li>
        <li>three</li>
        <li>four</li>
    </ul>
)

const playerName = ['lbj','kb','kg','dw'];

const Player = ({match}) => (
    <div>
        <TopHeader member = {playerName} />
        <div>
            <Switch>
                <Route path={match.path} exact render={() => <h1>这里是Player首页</h1> }/>
                <Route path={`${match.path}/:member`} component={PlayerProfile}/>
            </Switch>
        </div>
    </div>
)

const PlayerProfile = ({match}) => (
    <h1 userid = {match.params.member}>this is {match.params.member}'s Subpage page!</h1>
)

class TopHeader extends React.Component{
    constructor(props){
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(event){
        console.log(event.target.innerText);
        console.log(location);
        location.href += `/${event.target.innerText}`;
    }
    render(){
        return(
            <div>
                <ul>
                    {this.props.member.map((item,key)=>(
                        // <p key = {key} onClick={this.handleClick}>{item}</p>
                        <li key = {key}><Link to={`/player/${item}`}>{item}</Link></li>
                    ))}
                </ul>
            </div>
        )
    }
}

const App = () => (
    <HashRouter>
        <PageLayout />
    </HashRouter>
)

ReactDOM.render(<App />,document.getElementById('root'));

注意:本例中使用的是HashRouter,之前使用的是BrowserRouter,由于其存在不能直接进入子路由,而且不能在子路由下刷新页面,才换成的HashRouter

换上<BrowserRouter>之后会出现 2 个问题:

如果你不是通过服务器启动应用,因为chrome自身的安全机制,在本地环境下根本不能用chrome玩。这个不关键,我本地测试换个浏览器还不行么,本地起个服务器也不麻烦。

关键问题,刷新就是404。原因很简单,BrowserRouter 和 HashRouter 完全不同,前者利用H5的 history 接口,前台路由就是后台收到的路由,你点到其他页面一刷新,得,根本没这个文件,服务器也很无辜,到底让我渲染个啥?后台也可以简单的解决这个问题:甭管你请求的啥地址,我先把入口文件扔给你。node处理方式如下(需要express):

app.get('*',(request,response)=>{
  response.sendFile(path.resolve(__dirname,'../index.html'))
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值