React学习五(页面路由)

文章目录

  • 一、前端路由
  • 二、hash模式
  • 三、history模式
  • 四、两种路由方式的差异
  • 五、React的页面路由模块:react-router-dom的使用
    • 1、安装
    • 2、实现路由模式的组件(决定路由模式)
    • 3、Route组件
    • 4、Router组件
    • 5、Link和NavLink组件
      • (1)Link组件
      • (2)NavLink组件
    • 6、Redirect组件
    • 7、Switch组件
    • 8、withRouter组件
    • 9、404视图
  • 六、react-router-dom实现路由示例


一、前端路由

      路由:管理页面之间的关系,不同的页面的路径不同,但是都承载在一个HTML文件中,路径不同,会加载不同的内容。
SPA:单页面应用-渲染速度快。
1、从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下):
      ①记录当前页面的状态(保存或分享当前页的url,再次打开该url时,网页还是保存(分享)时的状态);
      ②可以使用浏览器的前进后退功能(如点击后退按钮,可以使页面回到使用ajax更新页面之前的状态,url也回到之前的状态)。
2、作为开发者,要实现这两个功能,我们需要做到:
      改变url且不让浏览器向服务器发出请求;
      监测 url 的变化;
      截获 url 地址,并解析出需要的信息来匹配路由规则。
3、前端路由的本质是监听url变化,然后匹配路由规则,无需刷新就可以显示相应的页面,目前单页面路由主要有以下两种方式。
我们路由常用的hash模式history模式实际上就是实现了上面的功能。

二、hash模式

      这里的 hash 就是指 url 尾巴后的 # 号以及后面的字符。这里的 # 和 css 里的 # 是一个意思。hash也称作锚点,本身是用来做页面定位的,它可以使对应 id 的元素显示在可视区域内。
      由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件,浏览器的进后退也能对其进行控制,所以人们在 html5 的 history 出现前,基本都是使用 hash 来实现前端路由的。
例如使用到的api:

window.location.hash = 'qq' // 设置 url 的 hash,会在当前url后加上 '#qq'
var hash = window.location.hash // '#qq'
window.addEventListener('hashchange', function(){
    // 监听hash变化,点击浏览器的前进后退会触发
})

三、history模式

      hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。
      hash 的传参是基于 url的,如果要传递复杂的数据,会有体积的限制,而history 模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中,可以监听浏览器的前进、后退事件(back、forward、go)。
      主要通过history.pushState/replceState向当前历史记录中插入状态对象state,在浏览器前进、回退、跳转等动作发生时触发popState事件,此时可以通过解析popState事件回调函数的event参数中的state对象,或者解析当前页面url来实现路由。
      建议解析url方式实现路由。如果没有在页面首次加载的时候设置pushState/replaceState,那么首页一般是没有state对象的,在执行浏览器动作时,如果回退到首页,那么当前history对象的state属性不存在,导致解析state报错
相关API:

 window.history.pushState(state, title, url)
// state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
// title:标题,基本没用,一般传 null
// url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,否则会抛出错误。url可以是绝对路径,也可以是相对路径。

例如:
      当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, ‘./qq/’),则变成 https://www.baidu.com/a/qq/,执行history.pushState(null, null, ‘/qq/’),则变成 https://www.baidu.com/qq/。

window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录
window.addEventListener("popstate", function() {
    // 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发
});
window.history.back() // 后退
window.history.forward() // 前进
window.history.go(1) // 前进一步,-2为后退两步,window.history.length可以查看当前历史堆栈中页面的数量

四、两种路由方式的差异

(1)方式的异同
①页面手动刷新,hash模式不会向服务器发送请求,history模式会;
②hash模式下url中的哈希值不会发送到服务器,history模式url全部会发送至服务器;
③设置location.hash和pushState都不会导致浏览器刷新;
④设置location.hash的时候会触发hashchange事件和popstate事件;
⑤仅当pushState函数设置url参数的值为hash格式时,浏览器动作发生会触发hashchange事件,尽管location.hash值为空;
⑥a标签锚点跳转可以设置hash,触发hashchange事件。

(2)注意的问题
      如果pushState的url为跨域网址,那么会报错,这样设计的目的是防止恶意代码让用户以为他们是在另一个网站上。

五、React的页面路由模块:react-router-dom的使用

1、安装

npm install react-router-dom

2、实现路由模式的组件(决定路由模式)

      其实就是路由的hash和history两种模式,并且这两个组件是路由的容器,必须在最外层
(1)HashRouter组件:实现hash模式的路由

// hash模式
ReactDOM.render(
	<HashRouter>
		<Route path="/" component={Home}/>
	</HashRouter>
)

(2)BrowserRouter组件:实现history模式的路由

// history模式
ReactDOM.render(
	<BrowserRouter>
		<Route path="/" component={Home} />
	</BrowserRouter>
)

3、Route组件

      路由的一个原材料,是控制路径对应显示的组件,即为实现路径和显示组件之间的映射。
Route的参数:
path:跳转的路径
component:对应路径显示的组件
render:可以自己写render函数返回具体的dom,而不需要去设置component
location:传递route对象,和当前的route对象对比,如果匹配则跳转
exact:匹配规则,默认值为false,true的时候则表示精确匹配。
注意:
不同版本的react-router-dom,Route的属性也不同
react-router-dom6.0以下的版本:

<Route path="/users" component={组件} render={返回dom} location="route对象" exact="匹配规则"/>

react-router-dom6.0(含6.0)以上的版本:

<Route path="/users" element={组件} render={返回dom} location="route对象" exact="匹配规则"/>

4、Router组件

      低级路由,适用于任何路由组件,主要和redux深度集成,使用必须配合history对象,使用Router路由的目的是和状态管理库如redux中的history同步对接。

<Router history={history}>
    ...
</Router>

5、Link和NavLink组件

      类似于< a >标签,两者都是跳转路由,NavLink的参数更多些。

(1)Link组件

      Link组件的api属性:
to:有两种写法,表示跳转到哪个路由。

//字符串写法
<Link to="/a" />
//对象写法
<Link to={{
	pathname: '/courses',
	search: '?sort=name',
	hash: '#the-hash',//路由模式
	state: { fromDashboard: true }
}}/>

replace:就是将push改成replace。
innerRef:访问Link标签的dom。

(2)NavLink组件

      增加高亮导航,为选中的元素添加class名称:active。
      包含了Link组件的所有api,在Link组件的基础上进行了扩展。
      NavLink的api属性:
Link的所有api
activeClassName 路由激活的时候设置的类名
activeStyle 路由激活设置的样式
exact 参考Route,符合这个条件才会激活active类
strict 参考Route,符合这个条件才会激活active类
isActive 接收一个回调函数,active状态变化的时候回调触发,返回false则中断跳转

const oddEvent = (match, location) => {
  console.log(match,location)
  if (!match) {
    return false
  }
  console.log(match.id)
  return true
}
<NavLink isActive={oddEvent} to="/a/123">组件一</NavLink>

location 接收一个location对象,当url满足这个对象的条件才会跳转

<NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123" }}/>

6、Redirect组件

      页面重定向,属性和Link相同。

// 基本的重定向
<Redirect to="/somewhere/else" />
// 对象形式
<Redirect
	to={{
		pathname: "/login",
		search: "?utm=your+face",
		state: { referrer: currentLocation }
	}}
/>
// 采用push生成新的记录
<Redirect push to="/somewhere/else" />
// 配合Switch组件使用,form表示重定向之前的路径,如果匹配则重定向,不匹配则不重定向
<Switch>
	<Redirect from='/old-path' to='/new-path'/>
	<Route path='/new-path' component={Place}/>
</Switch>

注:
页面重定向:客户端向服务器端发送了两次请求
请求转发:客户端向服务器发送了一次请求

7、Switch组件

      进行路由切换,只会匹配第一个路由,类似Tab标签。Switch内部只能包含Route、Redirect、Router。
exact:精确匹配。

<Switch>
	<Route exact path="/" component={Home}/>
	<Route path="/about" component={About}/>
	<Route path="/:user" component={User}/>
	<Route component={NoMatch}/>
</Switch>

8、withRouter组件

      当一个非路由组件也想访问到当前路由的match,location,history对象,那么withRouter将是一个非常好的选择,可以理解为将一个组件包裹成路由组件。
      即包装器,将普通的组件包装成路由组件。包装后普通组件就可以访问路由信息(如:history、location、match)

import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
     const { match, location, history } = this.props
     return (
        <div>{props.location.pathname}</div>
    )
}
const FirstTest = withRouter(MyComponent);

9、404视图

      当用户访问一些不存在的URL时就该返回404视图了,但不存在的地址该如何匹配呢?----使用Switch
      Switch组件的作用类似于JS中的switch语句,当一项匹配成功之后,就不再匹配后续内容。这样的话就可以把要匹配的内容写在Switch组件中,最后一项写404视图,当其他都匹配不成功时就是404视图。例如:

<Switch>
	<Route exact={true} path={"/"} component={Home}/>
	<Route path={"/about"} component={About}/>
	<Route path={"/topics"} component={Topics}/>
	<Route component={View404}/>//View404为自己写的404页面
</Switch>

六、react-router-dom实现路由示例

React学习五(页面路由)_第1张图片
      首先安装react-router-dom。
Home.js:

import React,{ Component } from 'react';
class Home extends Component{
    render(){
        return (
            <div>
                <h2>Home页面</h2>
            </div>
        )
    }
}
export default Home;

About.js:

import React,{Component} from "react";
class About extends Component{
    render() {
        return (
            <div>
                <h2>About页面</h2>
            </div>
        )
    }
}
export default About;

Topic.js:

import React,{ Component } from "react";
class Topic extends Component{
    render() {
        console.log(this.props)
        return(
            <div>
                <h2>{ this.props.match.params.topicId}</h2>
            </div>
        )
    }
}
export default Topic;

Topics:

import React,{ Component } from "react";
import { Route,Link } from "react-router-dom";
import Topic from "./Topic";

class Topics extends Component{
  render() {
      console.log(this)
      return (
          <div>
              <h2>Topics</h2>
              <ul>
                  <li>
                      <Link to={`${this.props.match.url}/西安邮电大学`}>
                          西安邮电大学
                      </Link>
                  </li>
                  <li>
                      <Link to={`${this.props.match.url}/西安石油大学`}>
                          西安石油大学
                      </Link>
                  </li>
                  <li>
                      <Link to={`${this.props.match.url}/西安工业大学`}>
                          西安工业大学
                      </Link>
                  </li>
              </ul>
              <Route path={`${this.props.match.url}/:topicId`} component={ Topic }/>
              <Route path={ this.props.match.url }  render={()=>(
                  <h3>请求选择一个学校</h3>
              )}/>
          </div>
      )
  }
}
export default Topics;

App.js:

import logo from './logo.svg';
import './App.css';
import {BrowserRouter, Route, Link} from "react-router-dom";

import Home from "./components/Home";
import About from "./components/About";
import Topics from "./components/Topics";

function App() {
  return (
      <BrowserRouter>
        <div>
          <ul>
            <li>
              <Link to={"/"}>Home</Link>
            </li>
            <li>
              <Link to={"/about"}>About</Link>
            </li>
            <li>
              <Link to={"/topics"}>Topics</Link>
            </li>
          </ul>
          <hr/>
          <Route exact={true} path={'/'} component={ Home }/>
          <Route path={'/about'} component={ About }/>
          <Route path={ '/topics'} component={Topics }/>
        </div>
      </BrowserRouter>
  );
}
export default App;

React学习五(页面路由)_第2张图片

你可能感兴趣的:(React,react.js,学习,javascript)