react-router 入门笔记

React-router 笔记 官方文档

基本思路

  • react-router 通过react 组件的方式实现,
  • 路由相关的数据,通过props传递给组件调用,
  • 路由层级关系, 通过标签嵌套实现

基础标签

  • BrowserRouter : 路由容器
    • 该组件只能包含单个元素
  • Route : 组件渲染出口
    • 必须包含在 BrowserRouter 中
    • exact 精确匹配
  • Link : 跳转链接
    • 必须包含在 BrowserRouter 中

基本使用

// react-router-demo
import React, { Component } from 'react'
import {
    BrowserRouter as  Router,
    Route,
    Link
} from 'react-router-dom'

// component
import Btn from'./btn'

// pages

function home(props){

    return (
        <div className="home">
            home
        </div>
    )

}

function products(props){
    return (
        <div className="products">
            products
        </div>
    )
}

function about(props){
    return(
        <div className="about">
            about
        </div>
    )
}

function product(props){
   return (

    <div>
        product
    </div>

   )
}

// router-config

const routerConfig = [

    {
        label: 'home',
        path:'/',
        component: home,
        exact: true,

    },
    {
        label: 'products',
        path: '/products',
        component: products,
        exact: true,
    },
    {
        label: 'about',
        path: '/about',
        component: about
    },
    { 
        label: 'procut',
        path: '/products/:id',
        component: product
    }

]

// container

export default class pages extends Component{

    render(){

        const buildLabel = () =>{

            let tempalte = [];

            for(let item of routerConfig){
                tempalte.push(

                    <Btn key={item.label}>
                        <Link to={item.path}>
                            {item.label}
                        </Link>
                    </Btn>
                )
            }
            return tempalte
        }

        return (
            <Router>
                <div className='pages-container'>

                    <div className="tab">
                    {buildLabel()}
                    </div>

                    {
                        routerConfig.map((item) =>{
                            return (
                                <Route exact={item.exact} key={item.label} path={item.path} component={item.component}/>
                            )
                        })
                    }

                </div>

            </Router>
        )

    }

}

路由传参

  • 配置参数路径: path = '/:params'
  • 函数组件, 通过组件参数中的 match.params[paramName] 获取路由参数

// pages
function home(props){

    return (
        <div className="home">
            in home page
            <br/>
            router parmas: 
            <br/>
            { props.match.params.userId }
        </div>
    )

}

render(){

    return (
        <Router>
            <div className='pages-container'>

                <Link to='/userId'>
                    to user page
                </Link>

                <Route exact path='/' component={home}></Route>
                <Route path='/:userId' component={home}></Route>

            </div>
        </Router>
    )

}

重定向 Redirect

通过返回组件 Redirect 实现重定向

 function subPage(props){

    return (
        <Redirect to={{
            pathname: '/in_show_page'
        }}></Redirect>
    )

 }

return (
    <Router>
        <div className='pages-container'>

            <Link to='/subPage'>
                to sub page
            </Link>

            {/* 参数路由 */}
            <Route path='/:userId' component={home}></Route>

            {/* 重定向路由 */}
            <Route path='/subPage' component={subPage}></Route>

        </div>
    </Router>
)

命令式导航(history)

  • 命令式导航,通过history上的方法实现
  • 为props 添加 history 参数, 在组件内部获取路由相关的参数,及控制路由动作

withRouter

  • 对于 Route 绑定的组件,组要是页面,本身已经将 路由接口包裹在props中, 而其他组件想获取路由接口需要通过 withRouter(compoent) 处理.
  • withRouter 处理的组件必须包裹在 <Router> 标签中s, 也就是说, 子组件中路由参数等,来自于包裹的 Router 对象
// 使用 withRouter 处理组件,组件props中将包含 路由相关对象, { match, location, history }

// 定义组件
function jump (props){

    const { match, location, history } = props;
    console.log(props)

    return (

        <div>

            <button onClick={ history.goBack }> back </button>
            <button onClick={ () =>{history.push('/home')} }> jump to home </button>
            <button onClick={ () =>{ history.replace('/subPage') } }> replace path </button>

        </div>

    )

}

// 在父组件中构件

  render(){

    return (
        <Router>
        <div className="route-render">

            <Link to='/history'> history </Link>

            <Route path='/history' component={withRouter(jump)} ></Route>

        </div>
        </Router>
    )

}

跳转拦截 (Prompt)

  • 使用Prompt, 可在路由跳转前,执行相关操作

//跳转提示, 每次路由跳转,提示信息 
<Prompt message="路由将跳转"/>

//message 为函数
<prompt message={ lcoation => (`跳转地址 ${location.pathname}`) } />

// 带触发条件 when = Boolean
<Propmt when={ location.pathname === '/home' } message='你将进入home页面' />

// 

标签API

<Router>

  • history: 绑定history对象, 方便外部调用

<Route>

  • component: 渲染组件, 组件props将包含, { match, location, history } 路由参数
  • render: 通过函数渲染组件, 通过渲染简单组件的方式, 及通过该方式,为子组件配置参数
  • children: 构建自定义链接标签,
  • path: 路由匹配地址
  • exac: 是否精确匹配
  • stric: 使用严格模式

<Switch>

  • 多路径匹配时,只渲染就近配置路径下的组件

     /**
     * 路径为 '/' 只会渲染 home 组件
     */

    <Switch>

        <Route path='/' componet={home}/>
        <Route path='/about' componet={about}/>
        <Route path='/product' componet={product}/>

    </Switch>

路由参数

  • match

    • params :查询参数
    • isExac : 是否精确匹配
    • path : 包含 basename 路径
    • url: Link 地址
  • location

    • key: 'ac3df4', // 标识符
    • pathname: '/somewhere' //路由地址
    • search: '?some=search-string',
    • hash: '#howdy',
    • state: { [userDefined]: true }
  • history

    • length : 记录条数
    • action : 当前记录
    • location : location
    • push: 添加纪录,(跳转页面)
    • replace: 替换当前记录 (跳转页面)
    • go: 跳转到某条记录
    • goBack: 回退
    • goForward: 前进

路由嵌套

  • 我们知道路由组件都包含在 <BrowserRouter> 中, 且该标签只能包含单一子元素,我们可以认为该标签创建一个路由环境, 包含在该标签内的 路由组件无论层级数,都归属于该路由环境.

 //父组件
 <App>
    <BrowserRouter>
        <div>
            <Route path='/' component={Home} />           
             <Route path='/about' component={About} /> 

            <Sub/>
        </div>
    </BrowserRouter>
 </App>

 //子组件

<div className='sub'>
    <Link to='/about'> to about </Link>

    <Route path='/about'  rennder={ () =>( <div> in sub about </div> ) } />
</div>

/**
 * Sub中的路由组件 与App中的路由组件处于同一层级, 当点击 Link标签时, 将进入 About 而不是Sub的自定义组件
 */

  • 创建属于当前页的子路由需要,需要创建新的 '<BrowserRouter>' 标签, 在没有配置basename的情况下,子路由的路径将以上级路由路径为基础, 且优先匹配当前路由环境下的组件, 例如: 父组件路径: '/home' 子组件下有 <Route path='/sub'>, 实际路径为: '/home/sub' 所以在划分路径时, 需要注意路径嵌套的问题,如对根路径 '/' 的处理, 很可能出现,路由配置冲突。

//父路由

let AppRouter = [

  {
    label: 'home',
    path: '/',
    component: Home,
    exact: true
  },
  {
    label: 'user-center',
    path: 'user-center',
    component: UserCenter
  },
  {
    label: 'books',
    path: '/books',
    component: Books
  },
  {
    label: 'book',
    path: '/books:id',
    component: Books
  }

]

return (
      <Router>
        <div className="App">

            <Switch>
                {routes}
            </Switch>

        </div>
      </Router>
    );

//子路由

  <Router>
   <div>
        <h4>路由</h4>
        <Link to='/books'>  to sub </Link>

        <Route path='/' render={ () =>( <div> 路由嵌套 path='/' </div> ) }></Route>
        <Route path='/books' render={ () =>( <div> 路由嵌套 path='/sub' </div> ) }></Route>
   </div>
</Router>

 /*
 ** 这是个路由冲突的例子, 可以看到,在父组件和子组件中,都配置了路径 '/books', 
 ** 当触发 Link 跳转时,将显示自组件内的组件, 即显示: '路由嵌套,path=/sub'
 ** 看起来一切正常,但当我们刷新页面, 将进入主路由的 Books 组件, 所以对于这样的路由冲突,编写时不易发现
 */

component, rander, children 的区别

  • component 是应用最多的渲染接口,一般组件使用该接口就可以了, 该接口在渲染是将调用creatElement 构建组件
  • rander 接受一个渲染函数, 构建时直接调用函数返回的模板, 不会调用creatElement, 这里是与component不同的地方, rander主要用在需要为组件传递一些 props参数时使用, 如果我们在component 中传入匿名函数包裹的组件, 该组件将被反复调用, 应该creatELement函数无法对匿名函数做比较。参考: React router的Route中component和render属性的使用
  • children 无论路径是否匹配都将被渲染, 不同的是, 对于已匹配的路径,children 组件内将获取到 match 参数

自定义history

  • 一般在浏览器使用的路由为 BrowserRouter,该路由是封装后的Router,提供了默认的history,所以该路由没有history 接口, 我们可以使用Router 自定特定类型的Router

import { Router} from 'react-router-dom'
import { createBrowserHistory } from 'history'

// 创建history
const history = createBrowserHistory()

// 包装原方法, 添加日志功能
const go = history.go
history.go = n => {
    console.log(`go to --> ${n}`)
    go(n)
}

// 监听路由变化, 重定向
history.listen(( location, action ) => {

    const isLogin = false
    if( isLogin ) {
        setTimeout(() => {
            history.push("/login")
        })
    }

})

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容