react-react router5使用

loading 2022年12月09日 100次浏览

1. 一级路由与多级路由

通过npm下载好'react-router-dom'后建立一个文件写这么一段结构:

            <HashRouter>
                <Switch>
                    <Route path={'/film'} component={Film} />
                    <Route path={'/cinema'} component={Cinema} />
                    <Route path={'/center'} component={Center} />

                    {/* 404页 */}
                    <Route component={notFound}/>
                </Switch>
            </HashRouter>
  • HashRoute:最外层的结构,使得url自带/#,如:http://localhost:3000/#
  • Switch:如果没有Switch组件,当一个path匹配到多个component时,会渲染出多个组件。一个路径在匹配到了对应的component之后,还会接着向下匹配 ,会大大的影响效率。
    将Route组件包裹在Switch组件之中,当path匹配到一个component之后,将不会再想下继续匹配,提高了程序效率。
  • Route:提供path参数,即url路径;component参数,代表对应路径要渲染的组件

2. 路由重定向

比如url不加后缀时需求自动跳转到film页面,可以使用Redirect重定向。

            <HashRouter>
                {props.children}
                <Switch>
                    <Route path={'/film'} component={Film} />
                    <Route path={'/cinema'} component={Cinema} />
                    <Route path={'/center'} component={Center} />

                    {/* 重定向 精确匹配 */}
                    <Redirect from='/' to='/film' exact/>

                    {/* 404页 */}
                    <Route component={notFound}/>
                </Switch>
            </HashRouter>

语义比较清晰,简单实现了从无后缀跳转到film页

exact代表精确匹配 也就是url中必须刚好无后缀才跳转 不加的话所有路径都会跳转到film页 因为所有路径都以/开头

3. 嵌套路由

比如film页中嵌套两个小组件,正在上映和即将上映。

在组件中再写两个路由跳转以及若有需要写重定向即可:

export default function Film() {
    return (
        <div>
            film
            <Switch>
                {/* 嵌套路由 */}
                <Route path={'/film/nowplaying'} component={nowPlaying}></Route>
                <Route path={'/film/comingsoon'} component={comingSoon}></Route>

                {/* 嵌套重定向 */}
                <Redirect from='/film' to='/film/nowplaying'></Redirect>
            </Switch>
        </div>
    )
}

4. 声明式导航和编程式导航

比如需要实现一个tabbar组件,通过点击就可以实现跳转到不同页面,可以直接使用NavLink标签声明:

export default function tabBar() {
    return (
        <div>
            <ul>
                <li>
                    <NavLink to='/film'>film</NavLink>
                </li>
                <li>
                    <NavLink to='/cinema'>cinema</NavLink>
                </li>
                <li>
                    <NavLink to='/center'>center</NavLink>
                </li>
            </ul>
        </div>
    )
}

要注意的是NavLink必须放在Router标签中使用,因此可以在App.js中留下插槽,再将tabbar组件放进去。

export default class App extends Component {
    render() {
        return (
            <div>
		// 留下插槽
                <AppRouter>
                    <TabBar></TabBar>
                </AppRouter>
            </div>
        )
    }
}
export default function indexRouter(props) {
    return (
        <div>
            <HashRouter>
		// 在此插入
                {props.children}
                <Switch>
                    <Route path={'/film'} component={Film} />
                    <Route path={'/cinema'} component={Cinema} />
                    <Route path={'/center'} component={Center} />

                    {/* 重定向 精确匹配 */}
                    <Redirect from='/' to='/film' exact/>

                    {/* 404页 */}
                    <Route component={notFound}/>
                </Switch>
            </HashRouter>
        </div>
    )
}

然后再尝试在正在热映组件中实现两种导航方式,比如现在已经请求到了一批电影的数据,要实现点击电影名字就跳转到对应页面

    return (
        <div>
            {
                list.map(item =>
                    <li key={item.filmId}>
                        {/* 用声明式导航实现跳转 */}
                        <NavLink to={'/detail/'+item.filmId}>{item.name}</NavLink>
                    </li>
                )
            }
        </div>
    )

也可以通过编程式导航实现

    //编程式导航
    const history = useHistory()

    const changePage = (id) => {
        //方法1 原生js方法
        // window.location.href="#/detail/"+id

        // 方法2 通过route传过来的props提供的history跳转
        //props.history.push(`/detail/${id}`)

        //方法3 通过react-router-dom提供的useHistory() 其实和方法2一样 写法不同而已       
        history.push(`/detail/${id}`)
    }

    return (
        <div>
            {
                list.map(item =>
                    <li key={item.filmId} onClick={() => changePage(item.filmId)}>
                        {item.name}
                    </li>
                )
            }
        </div>
    )

5. 动态路由

第4点中实现了跳转,但是还没有实现接收到不同电影对应的不同filmId并进行相应处理,因此需要用到动态路由。

动态路由有两种方案:

建议使用方案一,方案二如果他人复制detail的url到浏览器中会报错,因为方案2需要一步一步跳转下来传参,其url是不带参数的;而方案一参数就在url内。

5.1 方案一

在path中加入/:xxx(xxx为自定义的名称)

            <HashRouter>
                {props.children}
                <Switch>
                    <Route path={'/film'} component={Film} />
                    <Route path={'/cinema'} component={Cinema} />
                    <Route path={'/center'} component={Center} />
                    {/* 动态路由方案1 */}
                    <Route path={'/detail/:filmId'} component={Detail} />

                    {/* 重定向 精确匹配 */}
                    <Redirect from='/' to='/film' exact/>

                    {/* 404页 */}
                    <Route component={notFound}/>
                </Switch>
            </HashRouter>

history.push()通过模板字符串拼接起来即可

history.push(`/detail/${id}`)

对应的detail组件中通过该方法获取信息

export default function Detail(props) {
    return (
        <div>
            {/* 动态路由方案1 */}
            {props.match.params.filmId}
        </div>
    )
}

5.2 方案二

path中不加/:xxx后缀,正常写

{/* 动态路由方案2 */}
<Route path={'/detail'} component={Detail} />

通过history.push()时加上对应参数

// 动态路由方案2 query传参(这里可以把query换成state)
history.push({pathname : '/detail' , query:{filmId:id}})

最后在detail组件中获取

export default function Detail(props) {
    return (
        <div>
            {/* 动态路由方案2(那么这里的query也要换成state) */}
            {props.location.query.filmId}
        </div>
    )
}

6. 路由拦截

比如点击center页面时,需要先登录才能进行更多的交互,比如查看我的信息,更换头像等等,则需要用到路由拦截。

先写一个鉴权函数(实际开发中具体分析)

function isLogin(){
    return localStorage.getItem('token')
}

然后将center的路由写法改成使用render()去执行,这样就可以在其中作表达式判断。同时也要新建个login的路由:

要注意的是这种写法不会自动Route传来的props,要得到的话需要在render参数中自己写上props,然后通过展开运算符传递属性。

{/* <Route path={'/center'} component={Center} /> */}

<Route path='/center' render={(props) => {
	return isLogin() ? <Center {...props}/> : <Redirect to='/login' />
}} />

<Route path='/login' component={Login} />

最后写一下login组件

export default function Login(props) {
  return (
    <div>
        <input></input>
	// 点击按钮后设置'token'并重定向到center页面
        <button onClick={() =>{
            localStorage.setItem('token' , 123)
            props.history.push(`/center`)
        }}>login</button>
    </div>
  )
}

7. withRouter

比如上面那种情况,子组件需要通过父组件传过来才能使用history等函数,那么假如父组件也没有Route给过来的props呢?可以通过withRouter来获取。

举个例子

    return (
        <div>
            {
                list.map(item =>
                    // 将item和props都传到子组件中
                    <FilmItem key={item.filmId} {...item} {...props}/>
                )
            }
        </div>
    )

相当于

    const WithFilmItem = withRouter(FilmItem)

    return (
        <div>
            {
                list.map(item =>
                    // 将item传到子组件中
                    <WithFilmItem key={item.filmId} {...item}/>
                )
            }
        </div>
    )

8. 路由模式

React-Router支持使用hash(对应 HashRouter)和browser(对应 BrowserRouter)两种路由规则:

  • BrowserRouter 创建的 URL 格式:xxx.com/path
  • HashRouter 创建的 URL 格式:xxx.com/#/path

8.1 BrowserRouter

它使用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。由此可以看出,BrowserRouter 是使用 HTML 5 的 history API 来控制路由跳转的

<BrowserRouter
    // 基准URL
    basename={string}
    // 如果为true 在导航过程中整个页面将会刷新 一般只在不支持HTML5 history的浏览器中使用
    forceRefresh={bool}
    // 用于确认导航的函数 默认window.confirm
    getUserConfirmation={func}
    // 用于设置location.key的长度
    keyLength={number}
/>

8.2 HashRouter

使用 URL 的 hash 部分(即 window.location.hash)来保持 UI 和 URL 的同步。由此可以看出,HashRouter 是通过 URL 的 hash 属性来控制路由跳转的

<HashRouter
    // 同上
    basename={string}
    // 同上
    getUserConfirmation={func}
    // 使用的hash类型
    hashType={string}  
/>