• 【React】React全家桶(七)路由简介与React Router 5


    1 路由的基本概念与原理

    1.1 路由

    路由是一个比较广义和抽象的概念,路由的本质就是一个映射关系(key: value),请求什么响应什么,key为路径,value可能为function或component,在开发中路由分为前端路由后端路由

    1.2 前端路由

    概念:根据不同的用户事件,显示不同的页面内容,value为component

    注册路由: 当浏览器的path变为/test时, 当前路由组件就会变为Test组件

    <Route path="/test" component={Test}> 
    
    • 1

    本质:用户事件与事件处理函数之间的对应关系

    在这里插入图片描述

    1.3 后端路由

    概念:根据不同的URL地址分发不同的资源,value为function

    注册路由: 当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

    router.get(path, function(req, res))
    
    • 1

    本质:URL 请求地址与服务器资源之间的对应关系

    在这里插入图片描述

    1.4 SPA

    由于后端渲染存在性能问题以及Ajax前端渲染不支持浏览器的前进后退操作,所以出现SPA技术

    SPA(Single Page Application)单页面应用程序:整个网站只有一个页面,内容的变化通过Ajax局部更新实现、同时支持浏览器地址栏的前进和后退操作,只有第一次会加载页面,以后的每次请求,仅仅是获取必要的数据,然后由页面中js解析获取的数据进行展示。

    实现前端路由:基于URL中的hash实现,hash的变化会导致浏览器记录访问历史的变化、但是hash的变化不会触发新的URL请求,点击菜单的时候改变URL的hash,根据hash的变化控制组件的切换。

    2 react-router-dom 5

    react-router-dom:

    • React的一个插件库
    • 专门用来实现一个SPA应用
    • 基于React的项目基本都会用到此库

    2.1 基本组件

    React Router中有三类内置组件:

    • router 组件
    • 路由匹配组件
    • navigation 组件(

    其它:

    • history对象
    • location对象
    • match对象
    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    安装包

    • 下载react-router-dom v5: npm install react-router-dom@5,若下载v6版本不需要加@5
      在这里插入图片描述

    • 引入bootstrap.css:

    • 安装完成后,上面所列出的这些组件,我们可以通过react-router-dom得到。

      import { BrowserRouter, Route, Link } from "react-router-dom";
      
      • 1

    2.2 router组件(BrowserRouter、HashRouter)

    基于React Router的web应用,根组件应该是一个router组件(BrowserRouter、HashRouter)。 react-router-dom提供了两种路由,两种路由都会创建一个history对象。如果我们的应用有服务器响应web的请求,我们通常使用组件;如果使用静态文件服务器,则我们应该使用 组件。

    import { BrowserRouter } from "react-router-dom";
    ReactDOM.render(
      // 路由组件将整个App包起来,保证使用的是同一个路由器
      <BrowserRouter>
        <App />
      </BrowserRouter>,
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    BrowserRouter和HashRouter区别:

    底层原理不一样:

    • BrowserRouter调用的是H5 history API,低版本兼容性问题
    • HashRouter 使用的是URL哈希值

    地址栏表现形式不一样:

    • BrowserRouter的路径:localhost:3000/demo/a

    • HashRouter的路径:localhost:3000/#/demo/a

    刷新后对路由state参数的影响不一样:

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

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

    官方会更推荐使用BrowserRouter,是因为其构建于H5的History API,比起hashRouter,它多出了更多的方法操控url。

    建议:服务路由使用BrowerRoute(网页跳转),前端路由使用HashRouter(局部更新)

    2.3 react-router路由模式

    路由需要实现三个功能:

    • 当浏览器地址变化时,切换页面;
    • 点击浏览器后退,前进按钮,网页内容发生变化;
    • 刷新浏览器,页面加载内容对应当前路由对应的地址;

    在单页面web网页中,单纯的浏览器地址改变,网页不会重载,如单纯的hash值改变,网页是不会变化的,因此我们的路由需要监听事件,并利用js实现动态改变网页。

    react-router路由模式:

    • hash 模式:监听浏览器地址hash值变化,并执行相应的js切换。
    • history 模式:利用H5 history API实现url地址改变,网页内容改变。

    2.3.1 hash模式

    使用window.location.hash 属性和window.onhashchange事件,可以监听浏览器hash值的变化,去执行相应的js切换网页。

    hash路由实现原理:

    1. hash 指的是地址中 # 号以及后面的字符。称为散列值
    2. 散列值不会随请求发送到服务器端的,所以改变hash,不会重新加载界面。
    3. 监听onhashchange事件,hash改变时,可以通过window.location.hash来获取和设置hash值。
    4. location.hash值的变化直接反应在浏览器的地址栏。

    2.3.2 history模式

    我们使用history首先应该了解window.history对象。它表示的是当前窗口的浏览历史,当发生改变时,只会改变路径,不会刷新界面。history对象就是一个堆栈

    方法:

    history.back():移动上一个网址,等同于浏览器的后退。

    history.forward():移动到下一个网址,等同于浏览器前进。

    history.go():接受一个参数,以当前网页为基准,来进行跳转。默认history.go(0),刷新当前界面。 history.go(-1) 相当于history.back( ), history.go(1) 相当于history.forward( );

    history.pushState():往history堆栈中添加一条记录。不会刷新界面,只会导致history对象变化,地址栏发生变化。

    history.replaceState():是替换当前history堆栈中最上层的记录。也是不会刷新界面,只会是histoty对象变化,地址栏发生变化。

    每当history对象发生变化,就会触发popState事件: window.addEventListener(“popState”,function( ){ })

    • 只调用pushState或者replaceState是不会触发该事件的,只有调用back,forward,go才会触发该事件。
    • 至此我们也能想到BrowserHisory是怎么利用history的API来实现url改变,网页内容发生改变。

    2.4 路由匹配组件(Route、Switch)

    react-router-dom中有两个匹配路由的组件:

    import { Route, Switch } from "react-router-dom";
    
    • 1

    路由匹配是通过将 组件的path属性当前的location的pathname进行比较来完成的。 当一个匹配了 ,它所对应的组件内容将被渲染出来。 不匹配,则渲染null。如果一个 没有path属性,他的组件对应内容将一直被渲染出来。

    // 当 location = { pathname: '/about' }
    <Route path='/about' component={About}/> // 路径匹配成功,渲染 组件
    <Route path='/contact' component={Contact}/> // 路径不匹配,渲染 null
    <Route component={Always}/> // 该组件没有path属性,其对应的组件会一直渲染
    
    • 1
    • 2
    • 3
    • 4

    我们可以在组件树的任何位置放置 组件。但是更常见的情况是将几个写在一起。 组件可以用来将多个“包裹”在一起。

    多个组件在一起使用时,并不强制要求使用组件,但是使用组件却是非常便利的,可以提高路由匹配效率(单一匹配)。会迭代它下面的所有子组件,并只渲染第一个路径匹配的,这样只要匹配到了第一个就不会再往下匹配了。

    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/contact" component={Contact} />
    </Switch>
    
    <Switch>
      // exact开启严格匹配,不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/contact" component={Contact} />
      {/* 如果上面的Route的路径都没有匹配上,则 被渲染,我们可以在此组件中返回404 */}
      <Route component={NoMatch} />
    </Switch>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    匹配时所显示的组件,有三种写法

    • component
    • render
    • children

    component

    在使用时,如果我们想渲染的内容已经有对应的组件,则可以直接使用component的方法。例如:

    <Route path="/user/:username" component={User} />;
    
    function User({ match }) {
      return <h1>Hello {match.params.username}!</h1>;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    render

    render方法直接使用一个内联函数来渲染内容。

    // convenient inline rendering
    <Route path="/home" render={() => <div>Home</div>}/>
    
    
    • 1
    • 2
    • 3

    2.5 Navigation组件(Link、NavLink、Redirect)

    React Router提供了一个组件用来在应用中添加link。 当渲染时, 一个标签在html页面中被创建出来。

    <Link to="/">Home</Link>
    // Home
    
    
    • 1
    • 2
    • 3

    组件是一个特殊的组件。当它的path与当前location匹配时,可以自定义其样式来表示当前页面位置。NavLink可以实现路由链接的高亮,通过activeClassName属性指定样式名,默认是"active"

    // location = { pathname: '/react' }
    <NavLink to="/react" activeClassName="hurray">
      React
    </NavLink>
    // React
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    当需要页面重定向时,我们可以使用 组件。当一个组件被渲染时,页面将被渲染到 组件的to属性指定的位置上。 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

    <Route exact path="/" render={() => (
      loggedIn ? (
        <Redirect to="/dashboard"/>
      ) : (
        <PublicHomePage/>
      )
    )}/>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.6 基本路由的使用

    效果图

    在这里插入图片描述

    路由的使用原理:在导航区点击导航链接引起路径的变化,路径的变化被路由器检测到进行匹配组件,在展示区进行展示

    路由的基本使用:

    1. 明确好界面中的导航区展示区
    2. 导航区的a标签改为Link标签, 当渲染时,一个标签在html页面中被创建出来。
      Demo
    3. 展示区写Route标签进行路径的匹配
    4. 基于React Router的web应用,根组件应该是一个router组件(BrowserRouter,HashRouter),故 的最外侧包裹了一个

    代码实现

    index.js

    //引入react核心库
    import React from 'react'
    //引入ReactDOM
    import ReactDOM from 'react-dom'
    //引入控制路由跳转BrowserRouter路由组件
    import {BrowserRouter} from 'react-router-dom'
    //引入App
    import App from './App'
    
    ReactDOM.render(
        //路由组件将整个App包起来,保证使用的是同一个路由器
        <BrowserRouter>
            <App/>
        </BrowserRouter>,
        document.getElementById('root')
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    App.jsx

    import React, { Component } from 'react'
    import { Link,Route } from 'react-router-dom'
    import Home from './Home'
    import About from './About'
    
    export default class App extends Component {
      render() {
        return (
          <div>
          <div className="row">
            <div className="col-xs-offset-2 col-xs-8">
              <div className="page-header"><h2>React Router Demo</h2></div>
            </div>
          </div>
          <div className="row">
            <div className="col-xs-2 col-xs-offset-2">
              <div className="list-group">
                {/* 在React中靠路由链接实现切换组件--编写路由链接, */}
                <Link className="list-group-item" to="/about">About</Link>
                <Link className="list-group-item" to="/home">Home</Link>
              </div>
            </div>
            <div className="col-xs-6">
              <div className="panel">
                  <div className="panel-body">
                    {/* 注册路由 */}
                    <Route path='/about' component={About} />
                    <Route path='/home' component={Home} />
                </div>
              </div>
            </div>
          </div>
        </div>
        )
      }
    }
    
    
    • 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
    • 37

    2.7 路由组件与一般组件的区别

    • 写法不同
      一般组件:
      路由组件:

    • 存放位置不同
      一般组件:components
      路由组件:pages

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

    //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"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    • public/index.html 中 引入样式时不写 .// (常用)【绝对路径】
    • public/index.html 中 引入样式时不写 ./%PUBLIC_URL% (常用)
    • 使用HashRouter

    push模式与replace模式:

     //push模式(默认):可以留下操作记录,不发生替换
     //replace模式:开启的路由不会留下操作记录,发生替换
     <Link replace to="/about">About</Link>
    
    
    • 1
    • 2
    • 3
    • 4

    2.8 嵌套路由使用

    效果图

    在这里插入图片描述

    注意:

    1. 注册子路由时要写上父路由的path值
    2. 路由的匹配是按照注册路由的顺序进行的

    代码实现

    Home/index.jsx

    import React, { Component } from 'react'
    import { Route, NavLink,Redirect,Switch } from 'react-router-dom'
    import News from './News'
    import Message from './Message'
    
    export default class Home extends Component {
      render() {
        return (
          <div>
            <h3>我是Home的内容</h3>
            <div>
              <ul className="nav nav-tabs">
                <li>
                  <NavLink className="list-group-item" to="/home/news">News</NavLink>
                </li>
                <li>
                  <NavLink className="list-group-item" to="/home/message">Message</NavLink>
                </li>
              </ul>
              <Switch>
                <Route path='/home/news' component={News} />
                <Route path='/home/message' component={Message} />
                <Redirect to='/home/news' />
              </Switch>
            </div>
          </div>
        )
      }
    }
    
    
    • 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

    2.9 向路由组件传递参数的方法

    效果图

    在这里插入图片描述

    2.9.1 方法一:传递params参数

    向路由组件传递params参数:

    • 路由链接(携带参数):详情
    • 注册路由(声明接收):
    • 接收参数:this.props.match.params

    :路由中的params参数类似于ajax中的params参数

    代码实现

    Message/index.jsx

    import React, { Component } from 'react'
    import { Link, Route } from 'react-router-dom';
    import Detail from './Detail';
    
    export default class Message extends Component {
      state = {
        messageArr: [
          { id: '01', title: '消息1' },
          { id: '02', title: '消息2' },
          { id: '03', title: '消息3' },
        ]
      }
      render() {
        const { messageArr } = this.state;
        return (
          <div>
            <ul>
              {
                messageArr.map((msgObj) => {
                  return (
                    <li key={msgObj.id}>
                      {/* 向路由组件传递params参数 */}
                      <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>                             {msgObj.title}</Link>
                    </li>
                  )
                })
              }
            </ul>
            <hr />
            {/* 声明接收params参数 */}
            <Route path="/home/message/detail/:id/:title" component={Detail}/>
          </div>
        )
      }
    }
    
    
    • 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

    Detail/index.jsx

    import React, { Component } from 'react'
    
    export default class Detail extends Component {
      state = {
        detailData : [
          { id: '01', content: '你好啊' },
          { id: '02', content: '还不错鸭' },
          { id: '03', content: '显示我吧' }
        ]
      }
      render() {
        console.log(this.props)
        // 接收params参数
        const { id, title } = this.props.match.params
        const findResult= this.state.detailData.find((dataObj) => {
          return dataObj.id === id
        })
        return (
          <div>
            <ul>
              <li>ID: {id }</li>
              <li>Title: {title }</li>
              <li>Content: { findResult.content}</li>
            </ul>
          </div>
        )
      }
    }
    
    
    • 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

    在这里插入图片描述

    2.9.2 方法二:传递search参数

    向路由组件传递search参数:

    • 路由链接(携带参数):详情

    • 注册路由(无需声明,正常注册即可):

    • 接收参数:this.props.location.search

    :获取到的searchurlencoded编码字符串,形式为key=value&key=value,需要借助querystring解析,路由中search参数类似于ajax中的query参数

    import qs from 'querystring'
    let obj = {name:'tom', age:18}
    console.log(qs.stringify(obj)) // name=tom&age=18
    let str = 'carName=Benz&price=199'
    console.log(qs.parse(str)) // {carName: 'Benz', price: 199}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代码实现

    Message/index.jsx

    import React, { Component } from 'react'
    import { Link, Route } from 'react-router-dom';
    import Detail from './Detail';
    
    export default class Message extends Component {
      state = {
        messageArr: [
          { id: '01', title: '消息1' },
          { id: '02', title: '消息2' },
          { id: '03', title: '消息3' },
        ]
      }
      render() {
        const { messageArr } = this.state;
        return (
          <div>
            <ul>
              {
                messageArr.map((msgObj) => {
                  return (
                    <li key={msgObj.id}>
                      {/* 向路由组件传递search参数 */}
                      <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>                      {msgObj.title}
                      </Link>
                    </li>
                  )
                })
              }
            </ul>
            <hr />
            {/* search参数无需声明接收,正常注册路由即可 */}
            <Route path="/home/message/detail" component={Detail}/>
          </div>
        )
      }
    }
    
    
    • 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
    • 37

    Detail/index.jsx

    import React, { Component } from 'react'
    import qs from 'querystring'
    
    export default class Detail extends Component {
      state = {
        detailData : [
          { id: '01', content: '你好啊' },
          { id: '02', content: '还不错鸭' },
          { id: '03', content: '显示我吧' }
        ]
      }
      render() {
        // 接收search参数
        const { search } = this.props.location.search
        const {id,title}=qs.parse(search.slice(1))//slice(1)去掉问号
        const findResult= this.state.detailData.find((dataObj) => {
          return dataObj.id === id
        })
        return (
          <div>
            <ul>
              <li>ID: {id }</li>
              <li>Title: {title}</li>
              <li>Content: {findResult.content}</li>
            </ul>
          </div>
        )
      }
    }
    
    
    • 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

    2.9.3 方法三:传递state参数

    向路由组件传递state参数:

    • 路由链接(携带参数):{ pathname:'/demo/test', state:{name:'tom',age:18} }}>详情
    • 注册路由(无需声明,正常注册即可):
    • 接收参数:this.props.location.state

    :刷新也可以保留住参数【history对象记录着在】

    代码实现

    Message/index.jsx

    export default class Message extends Component {
      render() {
        const {messageArr} = this.state
        return (
          <div>
            <ul>
              {
                messageArr.map((msgObj)=>{
                  return (
                    <li key={msgObj.id}>
          {/* 向路由组件传递state参数 */}
         <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>            {msgObj.title}
         </Link>
                    </li>
                  )
                })
              }
            </ul>
            <hr/>
            {/* state参数无需声明接收,正常注册路由即可 */}
            <Route path="/home/message/detail" component={Detail}/>
          </div>
        )
      }
    }
    
    
    • 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

    Detail/index.jsx

    import React, { Component } from 'react'
    
    export default class Detail extends Component {
      render() {
        // 接收state参数
        const {id,title} = this.props.location.state || {}
        const findResult = DetailData.find((detailObj)=>{
          return detailObj.id === id
        }) || {}
        return (
          <ul>
            <li>ID:{id}</li>
            <li>TITLE:{title}</li>
            <li>CONTENT:{findResult.content}</li>
          </ul>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.10 编程式路由导航

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

    • this.props.history.push( ):跳转留下记录

    • this.props.history.replace( ):跳转不留记录

      //push跳转 分别携带params、search、state参数
      this.props.history.push(`home/message/detail/&{id}/&{title}`)
      this.props.history.push(`home/message/detail?id=${id}&title=${title}`)
      this.props.history.push(`home/message/detail`,{id,titel})
      //replace跳转 携带params参数
      this.props.history.replace(`home/message/detail/&{id}/&{title}`)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • this.props.history.goBack( )

    • this.props.history.goForward( )

    • this.props.history.go(n):默认n=0,刷新当前界面, n=-1相当于goBack( ),n=1相当于goForward( )

    2.11 withRouter的使用

    import {withRouter} from 'react-router-dom'
    clss Header extends Compnent {
        ...
    }
    export default withRouter(Header)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    withRouter可以加工一般组件,让一般组件具备路由组件所特有的API,withRouter的返回值是一个新组件

  • 相关阅读:
    div+css网页html成品学生作业包含10个html页面——动漫主题海贼王
    Linux学习笔记---编辑器Vim
    vue-3d-model更改模型的背景颜色
    linux部署gitlab
    资源管理游戏模版进入The Sandbox
    【Linux网络编程】信号和定时器
    基于JavaSwing开发简单的画图板程序 课程设计 大作业源码
    关于软件设计师考试中的算法
    Spring boot实现定时器
    vue-cli安装与搭建SPA项目
  • 原文地址:https://blog.csdn.net/Better_Xing/article/details/126325488