React 教程第十篇 —— 路由(3.0)

路由

經過 URL 映射到對應的功能實現,React 的路由使用要先引入 react-router.js。
注意:
react-router 4.0 以上的版本和 3.0 及如下的版本有很大的差異,本教程使用的是 3.0.2 的版本,後續會更新 4.0 以上版本的教程。
在使用 npm 安裝時默認是安裝最新版本,若是安裝的版本是最新的,而使用上用的是 3.0 版本的用法,則會報錯。
因此在 npm 安裝時要指定版本 npm install react-router@3.0.2 --save-devjavascript

路由背景-SPA

傳統的前端基本都是經過頁面之間跳轉來實現各功能模塊的切換,這種作法會致使一個項目下來存在大量的 html 頁面,並且每一個頁面都有一大堆的靜態資源文件須要引入,在性能一直被垢病。後來有了隨着 ajax 的普及,還有 jQuery 對 ajax 的封裝後的便捷使用,開發者會大量的使用 ajax 來加載一個 html 頁面到當前頁面的某個容器當中來實現無刷新加載,但依然沒有解決大量存在 html 頁面和每一個頁面加載大量的靜態資源文件而致使性能上的問題。隨着移動互聯網的普及,移動端對頁面加載的性能要求和流量的限制愈來愈高,因此主流的前端框架都往 SPA 方向靠齊。
SPA,Single Page Application 的縮寫,單頁面應用,其目的是整個應用程序只有一個 html 頁面,結合構建 webpack 的統一打包思想,把全部靜態資源文件打包成一個 js 文件,在惟一的一個 html 頁面引用,從而真正意義上實現一個 html 文件,一個 js 文件完成一個應用的構想。
SPA 優化了靜態加載的性能,但一個應用程序仍是有不少的功能模塊,功能模塊之間的切換,就變成了組件之間的切換,因此到目前爲止基本上主流的前端框架都會有路由和組件兩個概念,並且實現思想都是一致的。html

路由引用與使用

//es5
var {Router, Route, hashHistory, Link, IndexRoute, browserHistory} = require("react-router");

//es6
import {Router, Route, hashHistory, Link, IndexRoute, browserHistory} from 'react-router';

//es5 和 es6 的使用都是同樣的
<Link to="/">Root</Link>
<Router>
    <Route path='/' component={RootComponent}/>
</Router>

//使用 `<script>` 標籤 
<script src="../js/ReactRouter.js"></script>
<ReactRouter.Link to="/">Root</ReactRouter.Link>
<ReactRouter.Router>
    <ReactRouter.Route path='/' component={RootComponent}/>
</ReactRouter.Router>

路由組件與屬性

Link

  • 用於路由之間跳轉,功能等同於 a 標籤。
  • 屬性 to 等同於 a 標籤的 href
  • <Link to="/page">page</Link>,做用等同於 <a href="#/page">page</a>

Router

  • 是最外層的路由組件,整個 Application 僅一個。
  • 屬性 history 有兩個屬性值:前端

    • hashHistory 路由將經過URL的hash部分(#)切換,推薦使用。
    • <Router history={hashHistory}> 對應的 URL 形式相似 example.com/#/some/path
    • browserHistory 這種狀況須要對服務器改造。不然用戶直接向服務器請求某個子路由,會顯示網頁找不到的404錯誤。
    • <Router history={browserHistory}> 對應的 URL 形式相似 example.com/some/path。

Route 組件的屬性

  • Route 是組件 Router 子組件,能夠經過嵌套 route 來實現路由嵌套。
  • 屬性 path:指定路由的匹配規則,這個屬性是能夠省略的,這樣的話,無論路徑是否匹配,老是會加載指定組件。
  • 屬性 component:指當 URL 映射到路由的匹配規則時會渲染對應的組件。
  • <Route path="/" component={RootComponent}/> 當 URL 爲 example.com/#/ 時會渲染組件 RootComponent
  • <Route path="/page1" component={Page1Component}/> 當 URL 爲 example.com/#/page1 時會渲染組件 Page1Component

基本用法

import React from 'react'
import ReactDOM from 'react-dom'
import {Router, hashHistory, browserHistory} from 'react-router'

const html = (
    <ul>
        <li><Link to="/">Root</Link></li>
        <li><Link to="/page">page</Link></li>
    </ul>
)

class RootComponent extends React.Component{
    render(){
        return (
            <div>
                <h1>RootComponent</h1>
                {html}
            </div>
        )       
    }
}

class PageComponent extends React.Component{
    render(){
        return (
            <div>
                <h1>PageComponent</h1>
                {html}
            </div>
        )       
    }
}

ReactDOM.render(
    <Router history={hashHistory}>
        <Route path='/' component={RootComponent}/>
        <Route path='/page' component={PageComponent}/>
    </Router>,
    document.getElementById('app')
)

效果預覽java

路由參數

  • 路由的參數傳遞是經過 Route 組件的 path 屬性來指定的。
  • 參數值可經過 this.props.params.paramName 來獲取。
  • :paramNamenode

    • 匹配URL的一個部分,直到遇到下一個/、?、#爲止。
    • <Route path="/user/:name">
    • 匹配 URL:/#/user/sam,參數 sam 爲必須存在。
    • this.props.params.name 的值爲 sam。
import React from 'react'
import ReactDOM from 'react-dom'
import {Router, hashHistory, browserHistory} from 'react-router'

class UserComponent extends React.Component{
    render(){
        return (
            <div>
                <h3>UserComponent 單個參數 </h3>
                <p>路由規則:path='/user/:username'</p>
                <p>URL 映射:{this.props.location.pathname}</p>
                <p>username:{this.props.params.username}</p>
            </div>
        )       
    }
}
ReactDOM.render(
    <Router history={hashHistory}>
        <Route path='/user/:username' component={UserComponent}/>
    </Router>,
    document.getElementById('app')
)
  • (:paramName)react

    • 表示URL的這個部分是可選的。
    • <Route path="/order(/:orderid)">
    • 匹配 URL:/#/order,this.props.params.orderid 獲取的值爲 undefined。
    • 匹配 URL:/#/order/001,this.props.params.orderid獲取參數的值爲 001。
import React from 'react'
import ReactDOM from 'react-dom'
import {Router, hashHistory, browserHistory} from 'react-router'

class UserComponent extends React.Component{
    render(){
        return (
            <div>
                <h3>OrderComponent 可選參數 </h3>
                <p>路由規則:path='/order(/:orderid)'</p>
                <p>URL 映射:{this.props.location.pathname}</p>
                <p>orderid:{this.props.params.orderid}</p>
            </div>
        )       
    }
}
ReactDOM.render(
    <Router history={hashHistory}>
        <ReactRouter.Route path='/order(/:orderid)' component={UserComponent}/>
    </Router>,
    document.getElementById('app')
)
  • *.*webpack

    • 匹配任意字符,直到模式裏面的下一個字符爲止。匹配方式是非貪婪模式。
    • <Route path="/all1/*.*">
    • this.props.params 獲取的參數爲一個固定的對象: {splat: [*, *]}
    • 匹配 URL:/all1/001.jpg,參數爲 {splat: ['001', 'jpg']}
    • 匹配 URL:/all1/001.html,參數爲 {splat: ['001', 'html']}
  • *git

    • 匹配任意字符,直到模式裏面的下一個字符爲止。匹配方式是非貪婪模式。
    • <Route path="/all2/*">
    • this.props.params 獲取的參數爲一個固定的對象: {splat: '*'}
    • 匹配 URL:/all2/,參數爲 {splat: ''}
    • 匹配 URL:/all2/a,參數爲 {splat: 'a'}
    • 匹配 URL:/all2/a/b,參數爲 {splat: 'a/b'}
  • **es6

    • 匹配任意字符,直到下一個/、?、#爲止。匹配方式是貪婪模式。
    • <Route path="/**/*.jpg">
    • this.props.params 獲取的參數爲一個固定的對象: {splat: [**, *]}
    • 匹配 URL:/all3/a/001.jpg,參數爲 {splat: ['a', '001']}
    • 匹配 URL:/all3/a/b/001.jpg,參數爲 {splat: ['a/b', '001']}

效果預覽github

IndexRoute

當訪問一個嵌套路由時,指定默認顯示的組件

AppComponent.js

import React from 'react'

export default class AppComponent extends React.Component{
    render(){
        return <div>{this.props.children}</div>
    }
}

LoginComponent.js

import React, {Component} from 'react'

export default class LoginComponent extends Component{
    login(){}
    render(){
        return <h1>Login</h1>
    }
}

HomeComponent.js

import React, {Component} from 'react'

export default class HomeComponent extends Component{
    login(){}
    render(){
        return <h1>Home</h1>
    }
}

Router.js

import React from 'react'
import {Route, IndexRoute} from 'react-router'

import AppComponent from '../components/app/app'
import HomeComponent from '../components/home/home'
import LoginComponent from '../components/login/login'

const routes = (
    <Route path="/" component={AppComponent}>
        <IndexRoute component={HomeComponent} />
        <Route path="login" component={LoginComponent} />
        <Route path="home" component={HomeComponent} />
    </Route>
)

export default routes;
  • 若是沒有加IndexRoute,則在訪問 http://localhost/#/ 時頁面是空白的
  • 訪問 http://localhost/#/login 纔會顯示內容
  • 加上 IndexRoute,在訪問http://localhost/#/時會默認渲染HomeComponent

模塊化

可利用組件Router的屬性routes來實現組件模塊化

router.js

import React from 'react'
import ReactDOM from 'react-dom'

import {Route, Router, IndexRoute, hashHistory} from 'react-router'

import AppComponent from '../components/app/app'
import HomeComponent from '../components/home/home'
import LoginComponent from '../components/login/login'

const routes = (
    <Route path="/" component={AppComponent}>
        <IndexRoute component={HomeComponent} />
        <Route path="login" component={LoginComponent} />
        <Route path="home" component={HomeComponent} />
    </Route>
)

ReactDOM.render(
    <Router history={hashHistory} routes={routes} />,
    document.getElementById('app')
)

編程式導航

  • 普通跳轉 this.props.router.push('/home/cnode')
  • 帶參數跳轉this.props.router.push({pathname: '/home/cnode', query: {name: 'tom'}})

路由鉤子函數

每一個路由都有enterleave兩個鉤子函數,分別表明用戶進入時和離開時觸發。

onEnter

進入路由/home前會先觸發onEnter方法,若是已登陸,則直接next()正常進入目標路由,不然就先修改目標路徑replace({ pathname: 'login' }),再next()跳轉。

let isLogin = (nextState, replace, next) => {
    if(window.localStorage.getItem('auth') == 'admin'){
        next()
    } else {
        replace({ pathname: 'login' })
        next();
    }
    
}
const routes = (
    <Route path="/" component={AppComponent}>
        <Route path="login" component={LoginComponent} />
        <Route path="home" component={HomeComponent} onEnter={isLogin}/>
    </Route>
)

onLeave

對應的setRouteLeaveHook方法,若是return true則正常離開,不然則仍是停留在原路由

import React from 'react'
import {Link} from 'react-router'

export default class Component1 extends React.Component{
    componentDidMount(){
        this.props.router.setRouteLeaveHook(
            this.props.route,
            this.routerWillLeave
        )
    }
    routerWillLeave(){
        return '確認要離開?'
    }
    render(){
        return (
            <div>
                <Link to="/login">Login</Ling>
            </div>
        )
    }
}
相關文章
相關標籤/搜索