IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React页面路由 -> 正文阅读

[JavaScript知识库]React页面路由

1 背景

随着 ajax 的使用越来越广泛,前端的页面逻辑开始变得越来越复杂,特别是单页Web应用(Single Page Web Application,SPA)的兴起,前端路由系统随之开始流行。

  • 从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下):
    • 记录当前页面的状态(保存或分享当前页的url,再次打开该url时,网页还是保存(分享)时的状态);
    • 可以使用浏览器的前进后退功能(如点击后退按钮,可以使页面回到使用ajax更新页面之前的状态,url也回到之前的状态);
  • 作为开发者,要实现这两个功能,我们需要做到:
    • 改变url且不让浏览器向服务器发出请求;
    • 监测 url 的变化;
    • 截获 url 地址,并解析出需要的信息来匹配路由规则。

我们路由常用的hash模式和history模式实际上就是实现了上面的功能。

2 hash模式

这里的 hash 就是指 url 尾巴后的 # 号以及后面的字符。这里的 # 和 css 里的 # 是一个意思。hash也称作锚点,本身是用来做页面定位的,它可以使对应 id 的元素显示在可视区域内。

由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件,浏览器的进后退也能对其进行控制,所以人们在 html5 的 history 出现前,基本都是使用 hash 来实现前端路由的。

缺点:传递参数的方式是在url后拼接,会有体积的限制

使用到的api:

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

3 history模式

history模式的优点:
(1)可以传递复杂的参数
(2)可以监听浏览器的前进、后退事件(back、forward、go)

history模式与hash模式的比较:
1、hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。
2、hash 的传参是基于url的,如果要传递复杂的数据,会有体积的限制,而history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。

相关API:

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

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; // 查看当前历史堆栈中页面的数量

示例:当前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/

3 React实现页面路由的模块:react-router-dom

3.1 安装

npm install react-router-dom

3.2 HashRouter和BrowserRouter:路由的容器

HashRouter和BrowserRouter决定了路由模式分别是hash模式和history模式,并且这两个组件是路由的容器,必须放在最外层。

1、hash模式

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

2、history模式

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

3.3 Route:路由与组件之间的映射

Route组件:实现路径和显示组件之间的映射

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

它的参数如下:

参数说明
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="匹配规则"/>

示例:

<Routes>
    <Route exact path={"/"} element={<home/>}/>
    <Route path={"/about"} element={<About/>}/>
    <Route path={"/topics"} element={<Topics/>}/>
</Routes>

3.4 Router:管理路由的状态

Router组件:底层路由,使用的前提是路由模式必须是history模式,可以管理路由的状态。

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

3.5 Link和NavLink:跳转链接

Link和NavLink组件:类似于<a>标签

1、Link组件的api属性:
(1)to:目标页面的路径,两种写法,表示跳转到哪个路由

// 字符串写法
<Link to="/a" />

// 对象写法
<Link to={{
    pathname: '/courses',
    search: '?sort=name',
    hash: '#the-hash',
    state: {fromDashboard: true}
}}/>

(2)replace:就是将push改成replace
(3)innerRef:访问Link标签的dom

2、NavLink组件的api属性:包含了Link组件的所有api,在Link组件的基础上进行了扩展
(1)Link的所有api
(2)activeClassName:路由激活的时候设置的类名
(3)activeStyle:路由激活设置的样式
(4)exact:参考Route,符合这个条件才会激活active类
(5)strict:参考Route,符合这个条件才会激活active类
(6)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>

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

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

3.6 Redirect:页面重定向

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>

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

3.7 Switch:路由切换

Switch组件:进行路由切换,类似Tab标签。Switch内部只能包含Route、Redirect、Router

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

3.8 withRouter:包装器

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);

3.9 Router Hooks:在函数组件中获取路由信息

在Router5.x中新增加了Router Hooks用于在函数组件中获取路由信息。使用规则和React的其他Hooks一致。
(1)useHistory:返回history对象
(2)useLocation:返回location对象
(3)useRouteMatch:返回match对象
(4)useParams:返回match对象中的params,也就是path传递的参数

import React from "react";
import {useHistory} from "react - router - dom";

function backBtn(props) {
    let history = useHistory;
    return <button onClick={() => {
        history.goBack();
    }}>返回上一页</button>
}

3.10 history对象

在每个路由组件中我们可以使用this.props.history获取到history对象,也可以使用withRouter包裹组件获取,在history中封装了push,replace,go等方法,具体内容如下:

History {
    length: number;
    action: Action;
    location: Location;
    push(path: Path, state?: LocationState): void; // 调用push前进到一个地址,可以接受一个state对象,就是自定义的路由数据
    push(location: LocationDescriptorObject): void; // 接受一个location的描述对象
    replace(path: Path, state?: LocationState): void; // 用页面替换当前的路径,不可再goBack
    replace(location: LocationDescriptorObject): void; // 同上
    go(n: number): void; // 往前走多少也页面
    goBack(): void; // 返回一个页面
    goForward(): void; // 前进一个页面
    block(prompt?: boolean | string | TransitionPromptHook): UnregisterCallback;
    listen(listener: LocationListener): UnregisterCallback;
    createHref(location: LocationDescriptorObject): Href;
}

3.11 404视图:请求资源不存在

404视图:404错误表示客户端请求的资源不存在。在react中请求的路径不存在(404),路由采用Switch组件进行切换

<Switch>
    <Route exact={true} path={"/"} component={Home}/>
    <Route path={"/about"} component={About}/>
    <Route path={"/topics"} component={Topics}/>
    <Route component={View404}/>
</Switch>

4 react-router-dom实现路由案例

4.1 【案例1】实现简单的路由跳转

4.1.1 效果展示

启动程序,默认是Home界面,这时的地址是:localhost:3000
在这里插入图片描述
点击About界面,跳转到About界面,这时地址是:localhost:3000/about
在这里插入图片描述
点击Topics界面,跳转到Topics界面,这时地址是:localhost:3000/topics
在这里插入图片描述
点击话题1,页面跳转,这时地址是:localhost:3000/topics/话题1
在这里插入图片描述
点击话题2,地址是:localhost:3000/topics/话题2
在这里插入图片描述
点击话题3,地址是:localhost:3000/topics/话题3
在这里插入图片描述

4.1.2 实现步骤

1、用WebStorm创建一个React项目react-demo,首先安装react-router-dom模块,这里安装5.2.0版本(最新版本中的Route组件用法与本版本不同,具体在上面已经说明):

npm install react-router-dom@5.2.0

2、在src文件夹下新建文件components,用来编写不同的组件。在components文件夹下新建文件home.js,编写主页文件:

import React from "react";

class Home extends React.Component {
    render() {
        return (
            <div>
                <h2>Home页面</h2>
            </div>
        );
    }
}

export default Home;

3、在components文件夹下新建about.js文件,代码如下:

import React from "react";

class About extends React.Component {
    render() {
        return (
            <div>
                <h2>About页面</h2>
            </div>
        )
    }
}

export default About;

4、在components文件夹下新建topic.js文件,具体代码如下:

import React from "react";

class Topic extends React.Component {
    render() {
        return (
            <div>
                <h2>
                    {/*使用大括号表示这里的参数不止一条,需要从别的地方获取*/}
                    {/*this.props.match包含了url的信息,其中params包含了我们给定的参数*/}
                    {this.props.match.params.topicId}
                </h2>
            </div>
        )
    }
}

export default Topic;

5、在components文件夹下新建文件topics.js,代码如下:

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

class Topics extends React.Component {
    render() {
        return (
            <div>
                <h2>今日话题</h2>
                <ul>
                    <li>
                        {/*Link表示要跳转到哪个页面,但并没有实现跳转,this.props.match.url获取到了当前页面的url*/}
                        <Link to={`${this.props.match.url}/话题1`}>
                            话题1:今天吃了什么
                        </Link>
                    </li>
                    <li>
                        <Link to={`${this.props.match.url}/话题2`}>
                            话题2:今天做核酸了吗
                        </Link>
                    </li>
                    <li>
                        <Link to={`${this.props.match.url}/话题3`}>
                            话题3:今天天气怎么样
                        </Link>
                    </li>
                </ul>
                {/*真正实现页面功能的是Route组件,exact表示默认显示当前信息*/}
                {/*这里写render函数表示在页面加载出来之后首先显示该信息*/}
                <Route exact path={this.props.match.url} render={() => (<h3>请选择今日话题</h3>)}/>
                {/*当点击上面的话题时,跳转到topic界面,:topicId表示请求参数*/}
                <Route path={`${this.props.match.url}/:topicId`} component={Topic}/>
            </div>
        );
    }
}

export default Topics;

6、最后一步:修改App.js部分代码,将组件显示在网页上,App.js代码如下:

import {BrowserRouter, Link, Route} from "react-router-dom";
import Home from "./components/home";
import About from "./components/about";
import Topics from "./components/topics";

function App() {
    return (
        <BrowserRouter>
            {/*编写Home、About、Topics页面的跳转*/}
            <div>
                <ul>
                    <li>
                        <Link to={"/"}>Home界面</Link>
                    </li>
                    <li>
                        <Link to={"/about"}>About界面</Link>
                    </li>
                    <li>
                        <Link to={"/topics"}>Topics界面</Link>
                    </li>
                </ul>
                {/*使用Route实现真正的页面跳转*/}
                {/*当前默认主页面是Home界面*/}
                <Route exact path={"/"} component={Home}/>
                <Route path={"/about"} component={About}/>
                <Route path={"/topics"} component={Topics}/>
            </div>
        </BrowserRouter>
    )
}

export default App;

4.2 【案例2】实现计数器

4.2.1 效果展示

首页,localhost:3000:
在这里插入图片描述
新闻,localhost:3000/news:
在这里插入图片描述
点击新闻2,localhost:3000/news/1002:
在这里插入图片描述
课程,localhost:3000/course:
在这里插入图片描述
点击大数据,localhost:3000/big-data
在这里插入图片描述
加入我们,该页面没有编写,因此只能看到地址的改变,localhost:3000/joinUs:
在这里插入图片描述

4.2.2 实现步骤

1、用WebStorm创建一个React项目demo,首先安装react-router-dom模块,这里安装5.2.0版本(最新版本中的Route组件用法与本版本不同,具体在上面已经说明):

npm install react-router-dom@5.2.0

2、在src文件夹下新建文件夹components和css,components用来存放编写的组件,css用来存放样式,在components文件夹下新建文件Headers.js,编写网页的头部,代码如下:

import React, {Component} from "react";
import {NavLink} from "react-router-dom";
import "../css/header.css";

class Headers extends Component {
    render() {
        <header>
            <nav>
                <ul>
                    <li>
                        <NavLink exact to={"/"}>首页</NavLink>
                    </li>
                    <li>
                        <NavLink to={"/news"}>新闻</NavLink>
                    </li>
                    <li>
                        <NavLink to={"/course"}>课程</NavLink>
                    </li>
                    <li>
                        <NavLink to={"/joinUs"}>加入我们</NavLink>
                    </li>
                </ul>
            </nav>
        </header>
    }
}

export default Headers;

在这里插入图片描述


3、在css文件夹下新建header.css文件,编写css样式,代码如下:

body {
    font-size: 16px;
    margin: 0;
    padding: 0;
}

ul {
    text-align: right;
    background-color: #eee;
    margin: 0;
}

ul li {
    display: inline-block;
    list-style: none;
    text-align: center;
    border-left: 1px solid #ccc;
}

a {
    text-decoration: none;
    color: #666;
    font-size: 1.5rem;
    padding: 0.8em 2em;
    display: block;
}

a:hover {
    color: #000;
}

a:active {
    background-color: #666;
    color: #fff;
}

4、在src文件夹下新建文件夹pages,主要存放页面组件,在该文件夹下新建Home.js文件,用来显示主页。主页是一个计数器,代码如下:

import React, {Component} from "react";
import Headers from "../components/Headers";
import "../css/home.css";

class Home extends Component {
    constructor(props) {
        super(props);
        // 设置状态机
        this.state = {
            count: 0
        }
    }

    // 编写增加函数
    add = () => {
        // preState表示当前状态state
        this.setState((preState) => {
            return {
                count: preState.count + 1
            }
        })
    }
    // 编写减函数
    sub = () => {
        this.setState((preState) => {
            return {
                count: preState.count - 1
            }
        })
    }

    // 异步函数
    async asyncAdd() {
        // 表示1秒后再加1
        await setTimeout(() => {
            this.setState((preState) => {
                return {
                    count: preState.count + 1
                }
            })
        }, 1000);
    }

    render() {
        return (
            <div className={"home"}>
                <Headers/>
                <h1>Count的值:{this.state.count}</h1>
                <div className={"flexContainer"}>
                    <button onClick={() => this.asyncAdd()}>等待1s再执行count+1</button>
                    <button onClick={this.add}>count+1</button>
                    <button onClick={() => this.sub()}>count-1</button>
                </div>
            </div>
        )
    }
}

export default Home;

在这里插入图片描述


5、在css文件夹下新建文件home.css,代码如下:

@keyframes rotate {
    0% {
        transform: rotate(0deg);
        left: 0px;
    }
    100% {
        transform: rotate(360deg);
        left: 0px;
    }
}

.home {
    text-align: center;
}

.logo {
    animation: rotate 10s linear 0s infinite;
}

button {
    background: #237889;
    font-size: calc(1.5 * 1rem);
    color: #fff;
    padding: 0.3rem 1rem;
    border-radius: 1em;
    margin: 1em;
}

6、在pages文件夹下新建文件NewDetails.js,编写新闻详情页,代码如下:

import React, {Component} from "react";
import Headers from "../components/Headers";

class NewDetails extends Component {
    constructor(props) {
        super(props);
        // props.location.state存放了新闻的具体的数据id、content以及title
        this.data = props.location.state ? props.location.state.data : null;
    }

    render() {
        if (this.data != null) {
            let title = this.data.title;
            let content = this.data.content;
            return (
                <div>
                    <Headers/>
                    <h1>{title}</h1>
                    <p>{content}</p>
                </div>
            )
        }
    }
}

export default NewDetails;

在这里插入图片描述


7、在pages文件夹下新建News.js,编写新闻主页,代码如下:

import React, {Component} from "react";
import {Route, NavLink} from "react-router-dom";
import Headers from "../components/Headers";
import NewDetails from "./NewDetails";

const data = [ // 定义新闻的内容,在实际开发中这些数据来源于后台
    {
        id: 1001,
        title: "新闻1",
        content: "北京"
    }, {
        id: 1002,
        title: "新闻2",
        content: "上海"
    }
]

class NewsPage extends Component {
    render() {
        return (
            <div>
                <Headers/>
                <h1>请选择一条新闻</h1> 
                {
                    data.map((item) => {
                        return (
                            <div key={item.id}>
                                <NavLink to={{
                                    pathname: `${this.props.match.url}/${item.id}`,
                                    state: {data: item}
                                }}>
                                    {item.title}
                                </NavLink>
                            </div>
                        )
                    })
                }
            </div>
        )
    }
}

// Route组件实现真正的跳转
const News = ({match}) => {
    return (
        <div>
            <Route exact path={match.path} render={(props) => <NewsPage {...props}/>}/>
            <Route path={`${match.path}/:id`} component={NewDetails}/>
        </div>
    )
}

export default News;

在这里插入图片描述


8、在pages文件夹下新建文件Course.js,代码如下:

import React, {Component} from "react";
import Headers from "../components/Headers";
import {NavLink} from "react-router-dom";

class Course extends Component {
    render() {
        let {match} = this.props; // this.props.match存放了path和url
        return (
            <div>
                <Headers/>
                {/*浏览器的路径改变了,但是没有发生页面跳转*/}
                <p>
                    <NavLink to={`${match.url}/front-end`}>前端技术</NavLink>
                </p>
                <p>
                    <NavLink to={`${match.url}/big-data`}>大数据</NavLink>
                </p>
                <p>
                    <NavLink to={`${match.url}/algorithm`}>算法</NavLink>
                </p>
            </div>
        );
    }
}

export default Course;

在这里插入图片描述


9、最后一步,在App.js文件中修改部分代码,具体代码如下:

import {BrowserRouter as Router, Route, Switch} from "react-router-dom";
import Home from "./pages/Home";
import Course from "./pages/Course";
import News from "./pages/News";

function App() {
    return (
        <Router>
            <Switch>
                <Route exact path={"/"} component={Home}/>
                <Route path={"/course"} component={Course}/>
                <Route path={"/news"} component={News}/>
            </Switch>
        </Router>
    );
}

export default App;
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:22:38  更:2022-03-10 22:22:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:29:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码