学习react-router之基本路由

学习react-router之基本路由

八月 10, 2021

注意:这里的React-Router适用于"react-router-dom": "^5.2.0"

代码仓库skyone-wzw/learn-react-router

同时注意一下,react-router-dom依靠react-router,也就是说安装了前者就不需要安装后者了

运行环境:

OS:windows10

node:v14.17.3

IDE:webstorn

包管理器:yarn

这次我们的目标是创建一个拥有简单路由的页面

包含三个链接和一个框框,要求在不产生新的HTTP请求的情况下加载三个普通页面和一个404页面,三个链接不变,框框中显示页面的内容。

target

创建React工程

初始化工程

为了省事,直接使用create-react-app创建工程吧,

不需要安装任何前置组件,直接在你想要创建工程的文件夹运行

1
npx create-react-app ${你工程的名字}

目录结构就不多做介绍了,相比都知道,即使不知道多半也能猜出来

安装依赖

这里我安装两个库

@types/react

这个库用于辅助代码提示

react-router-dom

本文的主题啦,react的路由库

尝试运行

以下三个命令几乎是最常用的了,这里指出,后面不再说明

  • 运行dev版

    这样运行会监听文件修改并热更新,还会提供错误提示,如果浏览器安装了react插件,还能在检查元素中快速调试。

1
2
3
yarn start
#
npm start
  • 编译

    把代码编译成可以直接在浏览器上运行的代码

1
2
3
yarn build
#
yarn build
  • 运行简单的静态服务器

    编译代码后可以看看是否运行正常

1
server build

这里我们运行yarn start

浏览器弹出http://localhost:3000

create-react-app

编写代码

先把src目录的文件删了吧

index.js入口函数

然后我们创建应用的入口src/index.js

1
2
3
4
5
6
7
8
import ReactDOM from 'react-dom';
import App from "./App";

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

很简单嘛,就是把App加载到#root元素里嘛

App组件

创建App组件src/App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, {Component} from 'react';
import {Link} from "react-router-dom";

import "./default.css"

class App extends Component {
render() {
return (
<div>
<ul>
<li><Link to="/list">列表页</Link></li>
<li><Link to="/detail">详情页</Link></li>
<li><Link to="/404">404</Link></li>
</ul>
<div className="box"></div>
</div>
);
}
}

export default App;

从这里重点就要开始咯

App.jsx中的内容将是我们显示在主页的内容,包含三个链接和一个框框,当然这个类可以用函数代替

1
2
3
4
5
6
7
8
9
10
11
12
function App(props) {
return (
<div>
<ul>
<li><Link to="/list">列表页</Link></li>
<li><Link to="/detail">详情页</Link></li>
<li><Link to="/404">404</Link></li>
</ul>
<div className="box"></div>
</div>
)
}

但是rcc这个快捷指令多省事,干嘛不用呐?

Link就相当于是HTML中的a标签,完成页面的不刷新加载

我们再给box写一个样式吧,实验嘛,一切从俭好吧,懒字当头万事难

创建src/default.css

1
2
3
4
5
6
7
8
9
.box {
width: 250px;
height: 250px;
border: 1px solid black;
}

.notfound {
width: 250px;
}

这个notfound用于404的图片,这里先写出来

ListDetail组件

List组件用于渲染/list页面

上代码,src/List.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, {Component} from 'react';

class List extends Component {
render() {
return (
<div>
<h2>列表页</h2>
<button onClick={null}>查看详情</button>
</div>
);
}
}

export default List;

这个跳转按钮我们先不实现它的功能,先把要显示的东西写出来

Detail组件用于渲染/detail页面

src/Detail.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, {Component} from 'react';

class Detail extends Component {
render() {
return (
<div>
<h2>详情页</h2>
<button onClick={null}>返回列表</button>
</div>
);
}
}

export default Detail;

404页面

很简单,和上面的没有什么区别

只是导入了一个404.svg的图片而已,必须这样导入,只有这样webpack才能找到资源并打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, {Component} from 'react';
import {Link} from "react-router-dom";

import ErrSrc from "./assets/404.svg"
import "./default.css"

class NotFound extends Component {
render() {
return (
<Link to="/">
<img src={ErrSrc} className="notfound" alt="404 Not Found!"/>
</Link>
);
}
}

export default NotFound;

基本路由

路由文件一般只写路由逻辑,而不包含其他东西

src/router/BaseRouter.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, {Component} from 'react';
import {BrowserRouter, Switch, Route} from "react-router-dom";

import AppRouter from "./AppRouter";

class BaseRouter extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={AppRouter}/>
</Switch>
</BrowserRouter>
);
}
}

export default BaseRouter;

路由方式

react-router有三种路由方法:

  • HashHistory
  • BrowserRouter
  • MemoryHistory

官方建议使用BrowserRouter,原因是:好看。。。

至于三者有什么区别:

BrowserRouter就是浏览器原生的路由,例如http://localhost:3000/list就是/list

HashHistory则使用URL的hash值作为路由,例如http://localhost:3000/#/list表示/list,注意不要搞混了,URLPATH/就结束了,#后面的表示哈希(当然查询字符串?除外)

MemoryHistory暂时没学,不懂

使用路由

对于React-Router4,要使用路由,必须先选择路由方式

很简单,使用指定组件包住需要路由的最顶层目录(一般直接是根目录)就可以了,例如下面这个结构

1
2
3
4
5
<BrowserRouter>
<Switch>
<Route path="/" component={App}/>
</Switch>
</BrowserRouter>
  • 最外层的BrowserRouter定义里面的元素全部使用Browser路由,当然其他两种路由也是一样的道理。同时要注意,该组件的子元素无论如何嵌套,都不需要在使用该组件了。
  • Route就像是判断URL是否符合条件,只有符合path条件的才会渲染component里的元素
  • Switch组件包括的Route或其他组件只会渲染符合条件的第一个,后面的即使符合条件也不会渲染,值得注意的是,Switch里面不一定只能包含Route,也可以包含其他组件,但是其他组件没有符合条件一说,所以只要前面没有符合条件的Route就一定会渲染,当然后面的就不会渲染了,Switch只渲染一个子元素
  • Routeexact属性表示精确匹配,因为在同级组件下,不添加exact属性会导致path="/"会匹配到所有/开头的路由。例如:在不加exact属性的条件下path="/"会匹配到/abcpath="/asd"会匹配到/asd123;而精确匹配下exact path="/"只能匹配到/exact path="/asd"只能匹配到/asd

回头看我们写的代码,很简单,就是匹配到/开头的URL就运行AppRouter额,似乎有点多此一举。。。不过算了。

AppRouter组件

AppRouter是一个子路由,里面控制我们的三个URL//list/detail和特殊的/404

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
import React, {Component} from 'react';
import {Route, Switch, Redirect} from "react-router-dom";

import App from "../App";
import List from "../List";
import Detail from "../Detail";
import NotFound from "../NotFound";

class AppRouter extends Component {
render() {
return (
<App>
<Switch>
<Route exact path="/" component={() => <h2>Index</h2>}/>
<Route exact path="/list" component={List}/>
<Route exact path="/detail" component={Detail}/>
<Route exact path="/404" component={NotFound}/>
<Redirect to="/404"/>
</Switch>
</App>
);
}
}

export default AppRouter;

我们在App组件里放了个组件,读读这段代码,看看你理解Switch没有

1
2
3
4
5
6
7
8
9
<App>
<Switch>
<Route exact path="/" component={() => <h2>Index</h2>}/>
<Route exact path="/list" component={List}/>
<Route exact path="/detail" component={Detail}/>
<Route exact path="/404" component={NotFound}/>
<Redirect to="/404"/>
</Switch>
</App>

这段代码表示:

如果/就渲染一个h2标签

如果/list就渲染List组件

如果/detial就渲染Detial组件

如果/404就渲染NotFound组件

如果都不匹配,才渲染Redirect组件,跳转到/404

Switch只渲染一个组件!

修改App组件

由于 App组件有子元素,我们要指定子元素显示的位置,稍稍修改src/App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, {Component} from 'react';
import {Link} from "react-router-dom";

import "./default.css"

class App extends Component {
render() {
return (
<div>
<ul>
<li><Link to="/list">列表页</Link></li>
<li><Link to="/detail">详情页</Link></li>
<li><Link to="/404">404</Link></li>
</ul>
<div className="box">{this.props.children}</div>
</div>
);
}
}

export default App;

this.props.children即代表子元素

好啦,Ctrl + S保存,代码会自动被渲染,打开浏览器看看吧

虽然按钮功能还没有实现,但链接已经可以点了

实现按钮跳转

这里我只演示如何修改ListDetail自己改

src/List.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, {Component} from 'react';

class List extends Component {
constructor(props) {
super(props);
this.toDetail = this.toDetail.bind(this);
}

toDetail() {
this.props.history.push("/detail");
}

render() {
return (
<div>
<h2>列表页</h2>
<button onClick={this.toDetail}>查看详情</button>
</div>
);
}
}

export default List;

this.props.history.push("/detail");这一句表示跳转到/detail,和点击Link组件跳转是一模一样的。同样的方法还有

1
2
3
4
5
6
7
this.props.history.push(path: Path, state?: HistoryLocationState): void;
this.props.history.push(location: LocationDescriptor<HistoryLocationState>): void;
this.props.history.replace(path: Path, state?: HistoryLocationState): void;
this.props.history.replace(location: LocationDescriptor<HistoryLocationState>): void;
this.props.history.go(n: number): void;
this.props.history.goBack(): void;
this.props.history.goForward(): void;

看名字都能猜出来怎么用吧~~~~

基本和DOM的history差不多的

编译&&部署

编译很简单,一行命令

1
yarn build

至于部署嘛,要注意一下:

由于路由由React实现,页面本质上只有index.html,所以nginx配置文件需要少许修改,例如

/etc/nginx/conf.d/test.conf

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name localhost;

location / {
root "F://temp//nginx_root";
index index.html index.htm index.php;
try_files $uri /index.html;
}
}
target

想要向我的一样在本地实现SSL小绿锁也不是很难,下一篇文章我会写怎么配置的。

【完】