react路由

1.SPA理解

  1. 单页Web应用(single page web application,SPA)。即一个项目仅一个html页面,html上的内容可以数据发生变化。
  2. 整个应用只有一个完整的html页面,但是多组件。简记:单页面、多组件
  3. 点击页面中的链接不会刷新页面,只会做页面的局部更新。
  4. 数据都需要通过ajax请求获取, 并在前端异步展现。

2.路由理解

什么是路由?

(1)一个路由就是一个映射关系(key:value).。比如 浏览器地址栏 http://ip:port/home 那么 /home为key 对应的value为 Home组件或者后端服务器的某个方法;

(2)key为路径, value可能是function或component

路由分类

后端路由:

  • 理解: value是function, 用来处理客户端提交的请求。
  • 注册路由: router.get(path, function(req, res))
  • 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

前端路由:

  • 浏览器端路由,value是component,用于展示页面内容。
  • 注册路由: <Route path="/test" component={Test}>
  • 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

 3.前端路由的基石 history

        javascript 有三部分构成,ECMAScript,DOM和BOM。ECMAScript描述了JS的语法和基本对象。DOM(文档对象模型)处理网页内容的方法和接口。BOM(浏览器对象模型)与浏览器交互的方法和接口。

     在DOM中,HTML文档的层次结构被表示为一个树形结构。并用document对象表示该文档,树的每个子节点表示HTML文档中的不同内容。

     BOM的核心是Window,而Window对象又具有双重角色,它既是通过js访问浏览器窗口的一个接口,又是一个Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都以window作为其global对象。Window 对象代表一个浏览器窗口或一个框架。

 History对象

        window.history.length //浏览过的页面数

        history.back() //在浏览历史里后退一步

        history.forward() //在浏览历史里前进一步

        参考   Javascript之BOM与DOM讲解_晨港飞燕的专栏-CSDN博客

(1) 每一个页面,history都会将其对应的浏览器地址存入栈数据结构。

(2)地址栏显示的始终是history栈顶的地址。可以通过push方法入栈地址,那么地址栏地址也会发生改变,并且入栈会被history的listen方法监听到

(3)a标签的 onclick返回false,那么就不会跳转到href的地址。

<a href="http://www.atguigu.com" onclick="return push('/test1') ">push test1</a><br><br>

function push (path) {
	history.push(path)
	return false
}
function back() {
	history.goBack()
}
function forword() {
    history.goForward()
}
history.listen((location) => {
   console.log('请求路由路径变化了', location)
})

4.react-router-dom

react-router-dom的理解

  • react的一个插件库。
  • 专门用来实现一个SPA应用。
  • 基于react的项目基本都会用到此库。
  • react-route分为三个 ,用来开发web的、native应用的和开发两者通用的。react-route-dom为用来开发web应用的库。

使用步骤

 (1)安装web路由库 yarn add react-router-dom

 (2)根据路由库组件 进行开发

(1)基本路由的使用案例

        GitHub项目地址:https://github.com/MeBetterMan/react-study

 路由使用

    1)页面导航区编写路由链接

      导航区使用<Link>标签  to点击以后,浏览地址栏会改变

import { Link, Route } from 'react-router-dom'


{/* 原生html 靠a标签跳转不同页面 */}

    <a className="list-group-item" href="./about.html">About</a>
    <a className="list-group-item active" href="./home.html">Home</a> 

{/* 在react中通过路有链接实现切换组件--编写路由链接 */}

    <Link className="list-group-item" to="/about">About</Link>
    <Link className="list-group-item" to="/home">Home</Link>

    2)页面展示区编写 注册路由

      展示区使用<Route>标签注册路由;

       path不区分大小写,大写也是小写来看待,因此注册路由时小写字母。

       route路由,router路由器。

       注册路由必须在render方法的return虚拟DOM,点击路由链接后会在注册路由的位置显示路由链接对应的组件。

import { Link, Route } from 'react-router-dom'

 {/* 注册路由 */}
     <Route path='/about' component={About} />
     <Route path='/home' component={Home} />

Switch组件 

        上面的代码会遍历整个路由,寻找匹配的组件。

        下面的代码,如果不使用Switch组件包裹,/home会匹配两个组件,两个组件都会显示。

        遍历一遍路由,效率太低,而且没有必要,完全可以Home、Test组件组成一个组件。所以使用Switch组件包裹,Switch包裹的路由只会匹配第一个路由,找到一个以后不再继续遍历。

        Switch可以提高路由匹配效率(单一匹配)

import { Link, Route,Switch } from 'react-router-dom'

 {/* 注册路由 */}
<Switch>
     <Route path='/about' component={About} />
     <Route path='/home' component={Home} />
     <Route path='/home' component={Test} />
</Switch>

    3)注册路由器

    整个项目使用<BrowserRouter>标签

import { BrowserRouter } from 'react-router-dom'

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

注:

  •  一个项目只能有BrowserRouter标签。所以放在index.js包裹整个项目。
  • 如果一个项目多个BrowserRouter,那么相当于注册了多个路由器,路由器之间注册的路由不互通,会导致点击路由链接无法转移到路由的情况发生。

 一般组件和路由组件

  • 写法不同。

                   一般组件:<Home/> ,相当于自己写组件进行渲染;路由组件:<Route path="/demo" component={Demo}/>,由路由来选择渲染哪个组件,只要是组件被写到了路由注册中,那么路由便成了路由组件。

  • 存放位置不同

                  为了更好区分两类组件,习惯上一般组件放src/components文件夹下,路由组件放 src/pages文件夹下。

  • 接收到props不同。

                最大区别:一般组件写组件标签时传递了什么,就能收到什么;路由组件接收到history、location、match三个固定的属性

  • 一般组件通过withRouter变成路由组件

                 默认情况下必须经过路由匹配渲染的组件才存在this.props,才拥有路由参数,然而不是所有组件都直接与路由相连(通过路由跳转到此组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props。

history:
   go: ƒ go(n)
   goBack: ƒ goBack()
   goForward: ƒ goForward()
   push: ƒ push(path, state)
   replace: ƒ replace(path, state)
location:
   pathname: "/about"
   search: ""
   state: undefined
match:
   params: {}
   path: "/about"
   url: "/about"。

 NavLink标签

       Navlink标签可以在选中路由标签时,react自动追加activeClassName指定的类,从而让选中的标签显示高亮的效果。

import { NavLink, Route } from 'react-router-dom'

<NavLink activeClassName='demo' className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName='demo' className="list-group-item" to="/home">Home</NavLink>


<style>
.demo {
    background-color: blue !important;
    color: cornflowerblue !important;
}

总结

  • NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
  • 标签体内容是一个特殊的标签属性
  • 可以通过this.props.children获取标签体内容

封装NavLink

    上面使用NavLink,activeClassName、className都是一样的,冗余代码多。所以可以进行一次封装

1)封装NavLink

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'

export default class MyNavLink extends Component {
    render() {
        return (
            <NavLink activeClassName='demo' className="list-group-item" {...this.props}/>
        )
    }
}

2)使用自定义NavLink。标签体内容会自动传给 MyNavLink 

    扩展下去就是:可以对任何组件进行包装,去除冗余重复代码。

<MyNavLink  to='/about'>About</MyNavLink>
<MyNavLink to='/home'>Home</MyNavLink>

  • <MyNavLink to='/about'>About</MyNavLink> 标签体内容会被react自动封装为 props.children 属性传给MyNavLink组件.
<NavLink children='About'/>

等同于

<NavLink >About</NavLink >
  •  如上便实现了冗余代码都写到自己封装的组件里面。不同的属性直接在MyNavLink传递过去即可。 

解决多级路由路径刷新页面样式丢失的问题

问题

(1)多级路由地址
    <MyNavLink to='/fri/about'>About</MyNavLink>
    <MyNavLink to='/fri/home'>Home</MyNavLink>
(2)index.html 引入bootstrap.css样式
     <link rel="stylesheet" type="text/css" src="./css/bootstrap.css" />
(3)点击路由后,刷新页面,此时样式bootstrap.css无法正常请求回来

解决办法   

  1. public/index.html 中 引入样式时不写 ./ 写 / (常用)。./相当于相对路径,路由多级列表时,相对路径会相对最后一级路由前面的地址; /相当于相对于根目录
  2. public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)。仅react脚手架可以这么写
  3. 使用HashRouter。不常用

npm start启动后,http://ip:port/about  http://ip:port/相当于访问服务器

  • 服务器根目录为react的public文件夹。
  • 如果访问一个不存在的资源,会默认返回index.html

使用单个包管理工具 

npm yarn包管理器不要混着用 容易造成包丢失

路由的模糊、精确匹配

路由的模糊匹配 

{/* 注册路由 */}
<Route path='/about' component={About} />
<Route path='/home' component={Home} />
<Route path='/home/d/e' component={Home} />


{/* 在react中通过路有链接实现切换组件--编写路由链接 */}
<NavLink to='/about'>About</NavLink>
<NavLink to='/home/a/b'>Home</NavLink> //可以匹配到/home path的路由
<NavLink to='/home/'>Home</NavLink> //不可以匹配到/home/d/e path的路由

 规则

        to中的路由链接从头开始完全包括 path,且长度大于等于path,就算做匹配,这就是模糊匹配 。

        比如to 为 /home/d/e  可以匹配到的path为 /home 、/home/d、 /home/d/e 。不可以匹配 /d/e 因为为从头开始匹配

路由的精确匹配

    上面是模糊匹配,如果要开启精确匹配,需要注册路由时指定 exact={true},或者简写为exact。那么必须to path完全相同,才会匹配

{/* 注册路由 */}
<Route exact path='/about' component={About} />
<Route exact path='/home' component={Home} />

注:除非使用模糊匹配页面显示出现问题,否则不用精确匹配。开启精确路由会导致无法继续匹配二级路由。

Redirect组件

.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由。

注意:

  • 如果父子路由都有Redirect默认路由,那么必须使用Switch,否则进入子路由后,刷新浏览器,会匹配到父路由的redicrect,导致路由地址出现没有匹配到的路由错误。
  • 如果需要传递参数,记得Redirect也需要传递。尤其是编程式路由导航时不要忘记。否则,子组件获取参数时会报错。
<Switch>
    <Route path="/about" component={About}/>
    <Route path="/home" component={Home}/>
    <Redirect to="/about"/>
</Switch>

(2)嵌套路由的使用 

 Home组件return返回的虚拟DOM中包括News、Message组件,那么News、Message组件就是Home组件的子组件。

那么子组件可以放在src/pages/Home文件夹下,也可以src/pages/Home_News文件夹下,体现层级即可。两种方式都可以。

嵌套路由基本写法

 1.注册子路由时要写上父路由的path值。如果不写的话,点击就会进入一个新的页面,而不是在父路由页面的某部分显示。

 2.路由的匹配是按照注册路由的顺序进行的。 根据代码执行顺序,先执行的先注册。

 3.Home组件的虚拟DOM中使用Message组件,那么Message就是Home的子组件。

Home 父路由

  <Route path='/home' component={Home} />

Home/News 子路由 News为Home组件的组成路由

<Route path='/home/message' component={Message}></Route>
<Route path='/home/news' component={News}></Route>
<Redirect to='/home/news'></Redirect>

(3)路由传递参数

params参数

1)路由链接时,携带参数。

      语法: /变量值/变量值

      路由链接使用字符串模板传递参数数据,可以${}接收变量。

     模板字符串使用反引号。

      地址栏的地址链接中显示 变量值

<Link to='/demo/test/tom/18'}>详情</Link>

<Link to={`/home/message/${messageObj.id}/${messageObj.news}`}> {messageObj.news} </Link>

2)注册路由时,声明接收。

        注册路由时 使用变量接收路由链接发过来的数据 数据和变量名挂在了this.props.match.params 

        语法:  :变量名

 {/* 声明变量接收params参数 :变量名。 此时就默认传给了组件的props属性 */}
<Route path='/home/message/:id/:news' component={Detail}></Route>

 3)子组件接收参数。

const { id, news } = this.props.match.params

 search参数

1)路由链接(携带参数)   

        语法: ?属性名=值&属性名=值.

        地址栏的地址链接中显示变量名=变量值

<Link to='/demo/test?name=tom&age=18'}>详情</Link>

<Link to='/demo/test?name=${person.name}&age=${person.age}'}>详情</Link>

2)注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

3)接收参数:this.props.location.search

备注:获取到的search是urlencoded编码字符串,需要借助安装react时自带的querystring库解析。即无需单独安装querystring库

import qs from 'query-string'

const { id, news } = qs.parse(this.props.location.search.slice(1))

state参数

1)路由链接(携带参数)   

        {{}} to需要传递一个对象,第一层{}表示是一个js表达式 第二个{}表示是一个对象

        地址栏中不显示变量、变量值,隐性传递。

        react中return的虚拟dom中的对象,是通过render方法return之前定义的。比如下面的person对象。

<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

<Link to={{pathname:'/demo/test',state:{name:{person.name},age:{person.age}}}}>详情</Link>

2) 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

3)接收参数:this.props.location.state

备注:三种传参形式刷新浏览器也可以保留住参数

(4)路由的push和replace模式

 1)默认push 如果replace需要编写路由链接时自动replace属性 

<Link replace={true}  to={'/home/message/detail'}>Home</Link>

可简写

<Link replace  to={'/home/message/detail'}>Home</Link>

2)push会将每次路由地址压入history的栈中,然后通过浏览器的回退功能一个个显示出来;replace直接将新的路由覆盖栈顶元素,那么回退时就没有这个地址了。

(5)编程式路由导航

 不通过点击实现路由跳转,直接通过编码实现路由跳转。

通过操作路由组件的history对象来实现

借助this.prosp.history对象上的API对操作路由跳转、前进、后退                        

history:
   go: ƒ go(n) n表示前进后退几步
   goBack: ƒ goBack()
   goForward: ƒ goForward()
   push: ƒ push(path, state) 新路由地址不断想栈中存入路由地址,回退时后进先出
   replace: ƒ replace(path, state)  新路由地址直接覆盖栈顶路由地址 回退时会发现前面的路由地址不存在了

push路由地址到栈顶 此时页面就会跳转到push进去的路由地址 页面显示的始终是history栈顶的页面

  //params参数
 this.props.history.push(`/home/message/detail/${id}/${news}`)
  //search参数
 this.props.history.push(`/home/message/detail?id=${id}&news=${news}`)
  //state参数
 this.props.history.push(`/home/message/detail`,{id,news})

(6)withRouter

        默认情况下必须经过路由匹配渲染的组件才存在this.props,才拥有路由参数,执行this.props.history.push(’/detail’)跳转到对应路由的页面,然而不是所有组件都直接与路由相连(通过路由跳转到此组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props。

        Header组件为一般组件, 通过withRouter 方法就可以成为路由组件,就具备了路由组件的history match location 属性。

        react-router-dom 版本6.x已经没有这个函数,5.0.3版本有。

        在render方法的return返回的虚拟DOM里面注册路由,然后push方法实现路由跳转。

import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'

class Header extends Component {
    back = () => {
        this.props.history.goBack()
    }
    render() {
        return (
            <div className="row">
                <div className="col-xs-offset-2 col-xs-8">
                    <div className="page-header"><h2>React Router Demo</h2></div>
                </div>
                <button onClick={this.back}>后退</button>
            </div>
        )
    }
}

export default withRouter(Header)

(7)HashRouter BrowserRouter区别

  1.底层原理不一样:

              BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。常用这个。现在的react项目没必要支持旧版浏览器,因为react本身一些库也不支持旧版本。

              HashRouter使用的是URL的哈希值。

   2.path表现形式不一样

               BrowserRouter的路径中没有#,例如:localhost:3000/demo/test 

               HashRouter的路径包含#,例如:localhost:3000/#/demo/test 类似于锚点,不会根据路由地址重新给服务器发送请求,直接本地前端资源查找。

   3.刷新后对路由state参数的影响

            BrowserRouter没有任何影响,因为state保存在history对象中。

            HashRouter刷新后会导致路由state参数的丢失!!!

   4.HashRouter可以用于解决一些路径错误相关的问题。 比如多级路由样式表丢失。

5.lazyLoad 懒加载

如果一个页面导航区有很多个导航项,打开此页面时会默认全部导航项组件都加载,但是此时有些导航项可能并不会点击查看,造成网络浪费。 

 使用步骤

 1.引入lazy、Suspense

import React, { Component,lazy,Suspense} from 'react'

2.懒加载组件

        路由组件直接懒加载 不能够再直接import;但是Loading组件只能直接引入,用来当懒加载的组件响应慢时显示的组件

import Loading from './Loading'
const Home = lazy(()=> import('./Home') )
const About = lazy(()=> import('./About'))

3.注册路由时 使用Suspense包括

        fallback指定当懒加载的组件因响应慢还未返回时,显示的组件。

<Suspense fallback={<Loading/>}>
    {/* 注册路由 */}
	<Route path="/about" component={About}/>
	<Route path="/home" component={Home}/>
</Suspense>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fang·up·ad

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值