路由
在最后有一个小项目,里面用到的知识点都在前面,最后附上了项目地址
知识点
Web分类
- 静态页面:页面里的数据是写死的
- 动态页面:页面里的数据是动态填充的
- 后端渲染:数据在后端填充
- 前端渲染:数据在前端填充
前端渲染
是当客户端第一次访问网站的时候,就将该网站的所有数据全部传给客户端,当网站不同页面跳转的时候,并不需要再向后端发送请求
后端渲染
则是一个网站的不同页面的切换都要向后端发送请求重新渲染
当一个网站的不同页面跳转的时候是相同的url,就是前端渲染的,使用Route也是前端渲染,但是会给每一个页面都唯一对应一个url,因为网站和应用不一样,很多应用可能需要某个功能的时候一层层点进去,但是网页可以直接通过某个链接直接进入到特定页面。
安装环境
VSCODE安装插件:Auto Import - ES6, TS, JSX, TSX
安装Route组件:npm i react-router-dom
Route组件介绍
- BrowserRouter:所有需要路由的组件,都要包裹在BrowserRouter组件内
- Link:跳转到某个链接,to属性表示跳转到的链接
- Routes:类似于C++中的switch,匹配第一个路径
- Route:路由,path属性表示路径,element属性表示路由到的内容
URL中传递参数
解析URL:
<Route path="/linux/:chapter_id/:section_id/" element={<Linux />} />
获取参数,类组件写法:
import React, { Component } from 'react';
import { useParams } from 'react-router-dom';
class Linux extends Component {
state = { }
render() {
console.log(this.props.params);
return <h1>Linux</h1>;
}
}
export default (props) => (
<Linux
{...props}
params={useParams()}
/>
)
```函数组件写法:
```js
import React, { Component } from 'react';
import { useParams } from 'react-router-dom';
const Linux = () => {
console.log(useParams());
return (<h1>Linux</h1>);
}
export default Linux;
Search Params传递参数
类组件写法:
import React, { Component } from 'react';
import { useSearchParams } from 'react-router-dom';
class Django extends Component {
state = {
searchParams: this.props.params[0], // 获取某个参数
setSearchParams: this.props.params[1], // 设置链接中的参数,然后重新渲染当前页面
}
handleClick = () => {
this.state.setSearchParams({
name: "abc",
age: 20,
})
}
render() {
console.log(this.state.searchParams.get('age'));
return <h1 onClick={this.handleClick}>Django</h1>;
}
}
export default (props) => (
<Django
{...props}
params={useSearchParams()}
/>
);
函数组件写法:
import React, { Component } from 'react';
import { useSearchParams } from 'react-router-dom';
const Django = () => {
let [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get('age'));
return (<h1>Django</h1>);
}
export default Django;
重定向
使用Navigate组件可以重定向。
<Route path="*" element={ <Navigate replace to="/404" /> } />
嵌套路由
<Route path="/web" element={<Web />}>
<Route index path="a" element={<h1>a</h1>} />
<Route index path="b" element={<h1>b</h1>} />
<Route index path="c" element={<h1>c</h1>} />
</Route>
注意:需要在父组件中添加组件,用来填充子组件的内容。
新建一个项目叫route-app
- src目录下,除了index.css和index.js,其余文件删除
- 在src目录下新建一个components目录
- 进入components目录新建一个app.jsx和navbar.jsx文件
- navbar.jsx里面主要return一个导航栏组件,可以在bootstrap文档找一个,修改链接对应的url
- app.js里面引入NavBar,return一个Navbar组件
- 在index.js引入App组件,return 组件包裹在BrowserRouter内部
- App是根组件
index.js
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
navbar.js
class NavBar extends Component {
state = { }
render() {
return (
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">讲义</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="/">Home</a>
<a class="nav-link" href="/linux">Linux</a>
<a class="nav-link" href="/django">Django</a>
<a class="nav-link" href="/web">Web</a>
</div>
</div>
</div>
</nav>
);
}
}
app.js
class App extends Component {
state = { }
render() {
return (
<React.Fragment>
<NavBar />
</React.Fragment>
);
}
}
初始页面实现如下:
接下来,将Home Linux Django Web都做成一个组件
- 在components目录下新建四个对应四个名字的.jsx文件和一个notFound.jsx文件(对应找不到页面404)
- App是根组件,所以在app.js引入上面五个组件方便点击不同菜单时实现跳转,所有的组件要包裹在一个Routes组件下,五个组件分别在Route组件里作为element
- 前面还是后端渲染,将之前导航栏组件里的链接标签a换成Link标签,再次切换不同页面,后端就不会每次重新加载了,即前端渲染
目录
app.jsx
class App extends Component {
state = { }
render() {
return (
<React.Fragment>
<NavBar />
<div className='container'>
<Routes>
<Route path="/" element={<Home />}/>
<Route path="/linux" element={<Linux />}/>
<Route path="/django" element={<Django />}/>
<Route path="/web" element={<Web />}/>
</Routes>
</div>
</React.Fragment>
);
}
}
navbar.jsx
class NavBar extends Component {
state = { }
render() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<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 active" 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>
);
}
}
实现web页面
- 在web页面有五个讲义,在state里面定义讲义的id和title
- 在render里面渲染,用map,每一个目录都放在Link里
web.jsx
class Web extends Component {
state = {
webs : [
{id: 0, title: 'HTML'},
{id: 1, title: 'CSS'},
{id: 2, title: 'JavaScript'},
{id: 3, title: '拳皇'},
{id: 4, 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>
);
}
}
已经有了连接,接下来要通过每个url里面的id去渲染对应的页面,因为一个网页可能有上万个目录,不可能写这么多路由,所以一般url最后的id会变成一个变量,所以要在URL里传递参数
- 新建一个组件在webContent.jsx里,这个类组件接收URL传递的参数并显示
- 在App组件里增加一个Route组件,element为webContent组件,用来通过传递参数
第一种,通过URL传递参数
webContent.jsx
class WebContent extends Component {
state = { }
render() {
<React.Fragment>
<h1>Web - {this.props.params.chapter}</h1>
<hr />
<p>内容</p>
<Link to="/web">返回</Link>
</React.Fragment>
}
}
export default (props) => (
<WebContent
{...props}
params={useParams()}
/>
)
app.jsx
<Route path="/web/content/:chapter/" element={<WebContent />}/>
第二种:通过search Params传递参数
- 修改web里面Link标签的URL书写格式,改为?形式
- 修改app里面Route组件的path,匹配的部分保留,也就是问号前面的内容
web.jsx
<Link to={`/web/content?chapter=${web.id}`}>{web.id +"."+web.title}</Link>
app.jsx
<Route path="/web/content" element={<WebContent />}/>
webContent.jsx
class WebContent extends Component {
state = {
searchParams : this.props.params[0],
setSearchParams : this.props.params[1],
};
render() {
return (
<React.Fragment>
<h1>Web - {this.state.searchParams.get('chapter')}</h1>
<hr />
<p>内容</p>
<Link to="/web">返回</Link>
</React.Fragment>
);
}
}
export default (props) => (
<WebContent
{...props}
params={useSearchParams()}
/>
)
当一个页面不存在的时候,重定向到404
- 在app里面添加一个Route组件,element为NotFound组件
- 同时在最后添加一个Route组件,path为*,可以匹配所有路径
app.jsx
<Route path="/404" element={<NotFound />} />
<Route path="*" element={ <Navigate replace to="/404" /> } />
有时候可能一个页面里面有很多子页面,上半部分是不变的,但是下半部分会通过不同的路由显示不同的内容,这个时候就用到了嵌套路由
比如Linux页面有两个嵌套路由homework和terminal
- 在app里面的Linux对应的Route下面嵌套两个Route组件,但是这两个Route组件的path不用斜杠,直接写名字就行,会自动拼接到父组件的path下
- 想要渲染出来,在父组件中添加组件,用来填充子组件的内容,即在Linux组件添加
app.js
<Route path="/linux" element={<Linux />}>
<Route path="homework" element={<h4>homework的内容</h4>}></Route>
<Route path="terminal" element={<h4>terminal的内容</h4>}></Route>
</Route>
linux.js
render() {
return (
<React.Fragment>
<h1>Linux</h1>
<hr/>
<Outlet />
</React.Fragment>
);
}