• Web学习笔记-React(路由)


    笔记内容转载自 AcWing 的 Web 应用课讲义,课程链接:AcWing Web 应用课

    本节内容是如何将页面和 URL 一一对应起来。

    1. Web分类

    Web 页面可以分为两大类:

    • 静态页面:页面里的数据是写死的,即整个文件存放在服务器上,当用户访问 URL 时,服务器原封不动地将页面信息传给前端。
    • 动态页面:页面里的数据是动态填充的,即服务器上存的是页面的模板数据是存到数据库里的,当用户打开页面时,会动态将这个页面拼接起来。现在一般都是动态页面。
      • 后端渲染:数据在后端填充,即模板与数据的拼接操作是在服务器端进行的。客户端向服务器端发送 URL,服务器端返回拼接好的页面。
      • 前端渲染:数据在前端填充,即模板与数据的拼接操作是在用户的浏览器进行的。第一次打开页面时,客户端向服务器端发送 URL,服务器端返回所有页面的模板,渲染的时候根据当前需要哪些数据再向服务器端请求数据;第二次打开页面时,直接用 JS 刷新当前页面,不一定会向后端发送请求。

    2. Route组件

    Route 组件可以让我们的前端页面也可以和 URL 唯一对应起来,使得前端渲染的模式看起来假装和后端渲染是一样的。

    我们创建一个新的项目 route-app,然后用 VS Code 打开项目:

    create-react-app route-app
    
    • 1

    配置一下环境:

    • VS Code 安装插件:Auto Import - ES6, TS, JSX, TSX
    • 安装 Route 组件(在项目根目录下安装,安装好后重启一下 VS Code):npm i react-router-dom
    • 安装 Bootstrap:npm i bootstrap

    Route 组件介绍:

    • BrowserRouter:所有需要路由的组件,都要包裹在 BrowserRouter 组件内;
    • Link:跳转到某个链接(但是没有向后端发请求),to 属性表示跳转到的链接;
    • Routes:类似于 C++ 中的 switch,但是只匹配第一个路径,即从前往后看每个 Route,判断当前链接是否等于 Route 中的链接,如果是则渲染 Route 中的组件,之后的就不继续往下判断了;
    • Route:路由,path 属性表示路径,element 属性表示路由到的内容(组件)。

    我们先创建好我们项目的根组件 App导航栏 NavBar,以及多个子页面的组件:HomeLinuxDjangoWebNotFound

    NavBar 代码如下:

    import React, { Component } from 'react';
    
    class NavBar extends Component {
        state = {  } 
        render() {
            return (
                <nav className="navbar navbar-expand-lg bg-body-tertiary">
                    <div className="container-fluid">
                        <a className="navbar-brand" href="/">讲义</a>
                        <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                        </button>
                        <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
                            <div className="navbar-nav">
                                <a className="nav-link active" aria-current="page" href="/">Home</a>
                                <a className="nav-link" href="/linux">Linux</a>
                                <a className="nav-link" href="/django">Django</a>
                                <a className="nav-link" href="/web">Web</a>
                            </div>
                        </div>
                    </div>
                </nav>
            );
        }
    }
    
    export default NavBar;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    App 代码如下:

    import React, { Component } from 'react';
    import NavBar from './navbar';
    import Home from './home';
    import Linux from './linux';
    import Django from './django';
    import Web from './web';
    import NotFound from './notfound';
    
    class App extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <NavBar />
                </React.Fragment>
            );
        }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    HomeLinuxDjangoWebNotFound 代码类似,只展示一个:

    import React, { Component } from 'react';
    
    class Home extends Component {
        state = {  } 
        render() {
            return (
                <h1>Home</h1>
            );
        }
    }
    
    export default Home;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    现在我们根据 URL 来渲染页面,注意此时还是属于后端渲染,每次都会重新加载页面,我们修改 App

    import React, { Component } from 'react';
    import NavBar from './navbar';
    import Home from './home';
    import Linux from './linux';
    import Django from './django';
    import Web from './web';
    import NotFound from './notfound';
    import { Routes, Route } from 'react-router-dom'
    
    class App extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <NavBar />
                    <Routes>  // 一定要将路由包含在Routes里面,里面会有很多个Route
                        <Route path='/' element={<Home />} />  // 如果链接为'/'就跳到Home组件
                        <Route path='/linux' element={<Linux />} />
                        <Route path='/django' element={<Django />} />
                        <Route path='/web' element={<Web />} />
                    </Routes>
                </React.Fragment>
            );
        }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    现在我们用 Link 替换 NavBar 中的链接标签 a,这样就变为了前端渲染:

    import React, { Component } from 'react';
    import { Link } from 'react-router-dom'
    
    class NavBar extends Component {
        state = {  } 
        render() {
            return (
                <nav className="navbar navbar-expand-lg bg-body-tertiary">
                    <div className="container-fluid">
                        <Link className="navbar-brand" to="/">讲义</Link>
                        <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                        </button>
                        <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
                            <div className="navbar-nav">
                                <Link className="nav-link" aria-current="page" to="/">Home</Link>
                                <Link className="nav-link" to="/linux">Linux</Link>
                                <Link className="nav-link" to="/django">Django</Link>
                                <Link className="nav-link" to="/web">Web</Link>
                            </div>
                        </div>
                    </div>
                </nav>
            );
        }
    }
    
    export default NavBar;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3. URL中传递参数

    当网站的页面数量很多的时候,我们肯定不可能去写那么多个 Route

    假设我们现在有几篇 Web 讲义,第 i i i 篇的路由链接为:/web/content/i

    import React, { Component } from 'react';
    import { Link } from 'react-router-dom'
    
    class Web extends Component {
        state = {
            webs: [
                {id: 1, title: 'HTML基础标签'},
                {id: 2, title: 'CSS'},
                {id: 3, title: 'JavaScript'},
                {id: 4, title: '中期项目-拳皇'},
                {id: 5, title: 'React'},
            ]
        } 
        render() {
            return (
                <React.Fragment>
                    <h1>Web</h1>
                    <hr />
                    <div>
                        {this.state.webs.map(web => (
                            <div key={web.id}>
                                <Link to={`/web/content/${web.id}`}>{web.id + '.' + web.title}</Link>
                            </div>
                        ))}
                    </div>
                </React.Fragment>
            );
        }
    }
    
    export default Web;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    我们先实现一下讲义内容的组件 WebContent

    import React, { Component } from 'react';
    
    class WebContent extends Component {
        state = {  } 
        render() {
            return (
                <h1>Web Content</h1>
            );
        }
    }
    
    export default WebContent;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    然后在 App 中写一下路由(我们不能写多个 } />,而是用 :xxx):

    import React, { Component } from 'react';
    import NavBar from './navbar';
    import Home from './home';
    import Linux from './linux';
    import Django from './django';
    import Web from './web';
    import WebContent from './webcontent';
    import NotFound from './notfound';
    import { Routes, Route } from 'react-router-dom'
    
    class App extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <NavBar />
                    <div className='container'>
                        <Routes>  // 一定要将路由包含在Routes里面,里面会有很多个Route
                            <Route path='/' element={<Home />} />  // 如果链接为'/'就跳到Home组件
                            <Route path='/linux' element={<Linux />} />
                            <Route path='/django' element={<Django />} />
                            <Route path='/web' element={<Web />} />
                            <Route path='/web/content/:chapter' element={<WebContent />} />
                        </Routes>
                    </div>
                </React.Fragment>
            );
        }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    现在我们如何在 WebContent 中获取 :chapter 参数呢?先看一下函数组件获取参数的方式,可以直接用 useParams 函数获取参数:

    import React from 'react';
    import { useParams } from 'react-router-dom';
    
    const WebContent = () => {
        console.log(useParams())
        return (
            <h1>Web Content - {useParams().chapter}</h1>
        );
    }
    
    export default WebContent;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果是类组件的话就需要先套一层函数组件,然后把 useParams 函数作为参数传给自己:

    import React, { Component } from 'react';
    import { useParams } from 'react-router-dom';
    
    class WebContent extends Component {
        state = {  } 
        render() {
            console.log(this.props.params)
            return (
                <h1>Web Content - {this.props.params.chapter}</h1>
            );
        }
    }
    
    export default (props) => (
        <WebContent
            {...props}  // 先把函数组件里面的属性展开
            params={useParams()}
        />
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4. Search Params传递参数

    如果网站链接形式为:/web/content?chapter=3,这样的链接也可以获取参数。

    我们先改一下 Web 中的链接形式:

    import React, { Component } from 'react';
    import { Link } from 'react-router-dom'
    
    class Web extends Component {
        state = {
            webs: [
                ...
            ]
        } 
        render() {
            return (
                <React.Fragment>
                    <h1>Web</h1>
                    <hr />
                    <div>
                        {this.state.webs.map(web => (
                            <div key={web.id}>
                                <Link to={`/web/content?chapter=${web.id}`}>{web.id + '.' + web.title}</Link>
                            </div>
                        ))}
                    </div>
                </React.Fragment>
            );
        }
    }
    
    export default Web;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    然后在 WebContent 中获取链接的参数:

    import React, { Component } from 'react';
    import { useSearchParams } from 'react-router-dom';
    import { Link } from 'react-router-dom'
    
    class WebContent extends Component {
        state = {
            searchParams: this.props.params[0],  // 用于获取某一个参数
            setSearchParams: this.props.params[1],  // 用于设置链接里的参数,重新渲染页面
        };
        render() {
            console.log(this.state.searchParams.get('chapter'))
            return (
                <React.Fragment>
                    <h1>Web Content - {this.state.searchParams.get('chapter')}</h1>
                    <hr />
                    <div>讲义内容</div>
                    <hr />
                    <Link to='/web'>返回上一级</Link>
                </React.Fragment>
            );
        }
    }
    
    export default (props) => (
        <WebContent
            {...props}  // 先把函数组件里面的属性展开
            params={useSearchParams()}
        />
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    函数组件的写法如下:

    import React from 'react';
    import { useSearchParams } from 'react-router-dom';
    import { Link } from 'react-router-dom'
    
    const WebContent = () => {
        let [searchParams, setSearchParams] = useSearchParams();
        console.log(searchParams.get('chapter'));
        return (
            <React.Fragment>
                <h1>Web Content - {searchParams.get('chapter')}</h1>
                <hr />
                <div>讲义内容</div>
                <hr />
                <Link to='/web'>返回上一级</Link>
            </React.Fragment>
        );
    }
    
    export default WebContent;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5. 重定向

    当打开一个不存在的链接时应该重定向到 404 Not Found,我们先将这个路由定义出来:} />

    使用 Navigate 组件可以重定向,我们可以使用通配符 * 匹配其余的所有路径,然后将其重定向到 /404 页面即可:

    import React, { Component } from 'react';
    import NavBar from './navbar';
    import Home from './home';
    import Linux from './linux';
    import Django from './django';
    import Web from './web';
    import WebContent from './webcontent';
    import NotFound from './notfound';
    import { Routes, Route, Navigate } from 'react-router-dom'
    
    class App extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <NavBar />
                    <div className='container'>
                        <Routes>  // 一定要将路由包含在Routes里面,里面会有很多个Route
                            <Route path='/' element={<Home />} />  // 如果链接为'/'就跳到Home组件
                            <Route path='/linux' element={<Linux />} />
                            <Route path='/django' element={<Django />} />
                            <Route path='/web' element={<Web />} />
                            <Route path='/web/content' element={<WebContent />} />
                            <Route path='/404' element={<NotFound />} />
                            <Route path='*' element={<Navigate replace to='/404' />} />
                        </Routes>
                    </div>
                </React.Fragment>
            );
        }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    6. 嵌套路由

    假设 Linux 组件中有两个子模块 HomeworkTerminal,我们可以在 App 中创建嵌套路由:

    import React, { Component } from 'react';
    import NavBar from './navbar';
    import Home from './home';
    import Linux from './linux';
    import Django from './django';
    import Web from './web';
    import WebContent from './webcontent';
    import NotFound from './notfound';
    import { Routes, Route, Navigate } from 'react-router-dom'
    
    class App extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <NavBar />
                    <div className='container'>
                        <Routes>  // 一定要将路由包含在Routes里面,里面会有很多个Route
                            <Route path='/' element={<Home />} />  // 如果链接为'/'就跳到Home组件
                            <Route path='/linux' element={<Linux />}>
                                <Route path='homework' element={<h4>Homework</h4>} />
                                <Route path='terminal' element={<h4>Terminal</h4>} />
                            </Route>
                            <Route path='/django' element={<Django />} />
                            <Route path='/web' element={<Web />} />
                            <Route path='/web/content' element={<WebContent />} />
                            <Route path='/404' element={<NotFound />} />
                            <Route path='*' element={<Navigate replace to='/404' />} />
                        </Routes>
                    </div>
                </React.Fragment>
            );
        }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    但是现在执行网页 /linux/homework 时不会渲染出子路由的内容,我们需要在父组件中添加 组件,用来填充子组件的内容:

    import React, { Component } from 'react';
    import { Link, Outlet } from 'react-router-dom'
    
    class Linux extends Component {
        state = {  } 
        render() {
            return (
                <React.Fragment>
                    <h1>Linux</h1>
                    <hr />
    
                    <ul className="nav justify-content-center">
                    <li className="nav-item">
                        <Link className="nav-link" to="/linux/homework">Homework</Link>
                    </li>
                    <li className="nav-item">
                        <Link className="nav-link" to="/linux/terminal">Terminal</Link>
                    </li>
                    </ul>
                    <hr />
    
                    <Outlet />
                </React.Fragment>
            );
        }
    }
    
    export default Linux;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    6-5 头插法创建单链表(C) 分数 10
    Linux生产者消费者模型(POSIX信号量)
    如何查看多开的逍遥模拟器的adb连接端口号
    数据治理:为什么不见BI作关联分析
    v0.9.6 开源跨平台个人知识管理工具 TidGi-Desktop
    前端面试常见问题总结
    测吧(北京)科技有限公司项目总监王雪冬一行访问计算机学院探讨合作
    新能源电池试验中准确模拟高空环境大气压力的解决方案
    ROS 2知识:通信协议 DDS/RTPS
    【自动驾驶】ROS小车系统
  • 原文地址:https://blog.csdn.net/m0_51755720/article/details/132788417