筆記內(nèi)容轉(zhuǎn)載自 AcWing 的 Web 應(yīng)用課講義,課程鏈接:AcWing Web 應(yīng)用課。
本節(jié)內(nèi)容是如何將頁面和 URL 一一對應(yīng)起來。
1. Web分類
Web 頁面可以分為兩大類:
- 靜態(tài)頁面:頁面里的數(shù)據(jù)是寫死的,即整個(gè)文件存放在服務(wù)器上,當(dāng)用戶訪問 URL 時(shí),服務(wù)器原封不動(dòng)地將頁面信息傳給前端。
- 動(dòng)態(tài)頁面:頁面里的數(shù)據(jù)是動(dòng)態(tài)填充的,即服務(wù)器上存的是頁面的模板,數(shù)據(jù)是存到數(shù)據(jù)庫里的,當(dāng)用戶打開頁面時(shí),會(huì)動(dòng)態(tài)將這個(gè)頁面拼接起來?,F(xiàn)在一般都是動(dòng)態(tài)頁面。
- 后端渲染:數(shù)據(jù)在后端填充,即模板與數(shù)據(jù)的拼接操作是在服務(wù)器端進(jìn)行的。客戶端向服務(wù)器端發(fā)送 URL,服務(wù)器端返回拼接好的頁面。
- 前端渲染:數(shù)據(jù)在前端填充,即模板與數(shù)據(jù)的拼接操作是在用戶的瀏覽器進(jìn)行的。第一次打開頁面時(shí),客戶端向服務(wù)器端發(fā)送 URL,服務(wù)器端返回所有頁面的模板,渲染的時(shí)候根據(jù)當(dāng)前需要哪些數(shù)據(jù)再向服務(wù)器端請求數(shù)據(jù);第二次打開頁面時(shí),直接用 JS 刷新當(dāng)前頁面,不一定會(huì)向后端發(fā)送請求。
2. Route組件
Route 組件可以讓我們的前端頁面也可以和 URL 唯一對應(yīng)起來,使得前端渲染的模式看起來假裝和后端渲染是一樣的。
我們創(chuàng)建一個(gè)新的項(xiàng)目 route-app
,然后用 VS Code 打開項(xiàng)目:
create-react-app route-app
配置一下環(huán)境:
- VS Code 安裝插件:
Auto Import - ES6, TS, JSX, TSX
- 安裝 Route 組件(在項(xiàng)目根目錄下安裝,安裝好后重啟一下 VS Code):
npm i react-router-dom
- 安裝 Bootstrap:
npm i bootstrap
Route 組件介紹:
-
BrowserRouter
:所有需要路由的組件,都要包裹在BrowserRouter
組件內(nèi); -
Link
:跳轉(zhuǎn)到某個(gè)鏈接(但是沒有向后端發(fā)請求),to
屬性表示跳轉(zhuǎn)到的鏈接; -
Routes
:類似于 C++ 中的switch
,但是只匹配第一個(gè)路徑,即從前往后看每個(gè)Route
,判斷當(dāng)前鏈接是否等于Route
中的鏈接,如果是則渲染Route
中的組件,之后的就不繼續(xù)往下判斷了; -
Route
:路由,path
屬性表示路徑,element
屬性表示路由到的內(nèi)容(組件)。
我們先創(chuàng)建好我們項(xiàng)目的根組件 App
、導(dǎo)航欄 NavBar
,以及多個(gè)子頁面的組件:Home
、Linux
、Django
、Web
、NotFound
。
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;
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;
Home
、Linux
、Django
、Web
、NotFound
代碼類似,只展示一個(gè):
import React, { Component } from 'react';
class Home extends Component {
state = { }
render() {
return (
<h1>Home</h1>
);
}
}
export default Home;
現(xiàn)在我們根據(jù) URL 來渲染頁面,注意此時(shí)還是屬于后端渲染,每次都會(huì)重新加載頁面,我們修改 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里面,里面會(huì)有很多個(gè)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;
現(xiàn)在我們用 Link
替換 NavBar
中的鏈接標(biāo)簽 a
,這樣就變?yōu)榱饲岸虽秩荆?/p>
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;
3. URL中傳遞參數(shù)
當(dāng)網(wǎng)站的頁面數(shù)量很多的時(shí)候,我們肯定不可能去寫那么多個(gè) Route
。
假設(shè)我們現(xiàn)在有幾篇 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基礎(chǔ)標(biāo)簽'},
{id: 2, title: 'CSS'},
{id: 3, title: 'JavaScript'},
{id: 4, title: '中期項(xiàng)目-拳皇'},
{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;
我們先實(shí)現(xiàn)一下講義內(nèi)容的組件 WebContent
:
import React, { Component } from 'react';
class WebContent extends Component {
state = { }
render() {
return (
<h1>Web Content</h1>
);
}
}
export default WebContent;
然后在 App
中寫一下路由(我們不能寫多個(gè) <Route path='/web/content/i' element={<WebContent />} />
,而是用 :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里面,里面會(huì)有很多個(gè)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;
現(xiàn)在我們?nèi)绾卧?WebContent
中獲取 :chapter
參數(shù)呢?先看一下函數(shù)組件獲取參數(shù)的方式,可以直接用 useParams
函數(shù)獲取參數(shù):
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;
如果是類組件的話就需要先套一層函數(shù)組件,然后把 useParams
函數(shù)作為參數(shù)傳給自己:
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} // 先把函數(shù)組件里面的屬性展開
params={useParams()}
/>
);
4. Search Params傳遞參數(shù)
如果網(wǎng)站鏈接形式為:/web/content?chapter=3
,這樣的鏈接也可以獲取參數(shù)。
我們先改一下 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;
然后在 WebContent
中獲取鏈接的參數(shù):
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], // 用于獲取某一個(gè)參數(shù)
setSearchParams: this.props.params[1], // 用于設(shè)置鏈接里的參數(shù),重新渲染頁面
};
render() {
console.log(this.state.searchParams.get('chapter'))
return (
<React.Fragment>
<h1>Web Content - {this.state.searchParams.get('chapter')}</h1>
<hr />
<div>講義內(nèi)容</div>
<hr />
<Link to='/web'>返回上一級(jí)</Link>
</React.Fragment>
);
}
}
export default (props) => (
<WebContent
{...props} // 先把函數(shù)組件里面的屬性展開
params={useSearchParams()}
/>
);
函數(shù)組件的寫法如下:
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>講義內(nèi)容</div>
<hr />
<Link to='/web'>返回上一級(jí)</Link>
</React.Fragment>
);
}
export default WebContent;
5. 重定向
當(dāng)打開一個(gè)不存在的鏈接時(shí)應(yīng)該重定向到 404 Not Found
,我們先將這個(gè)路由定義出來:<Route path='/404' element={<NotFound />} />
。
使用 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里面,里面會(huì)有很多個(gè)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;
6. 嵌套路由
假設(shè) Linux
組件中有兩個(gè)子模塊 Homework
和 Terminal
,我們可以在 App
中創(chuàng)建嵌套路由:文章來源:http://www.zghlxwxcb.cn/news/detail-705926.html
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里面,里面會(huì)有很多個(gè)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;
但是現(xiàn)在執(zhí)行網(wǎng)頁 /linux/homework
時(shí)不會(huì)渲染出子路由的內(nèi)容,我們需要在父組件中添加 <Outlet />
組件,用來填充子組件的內(nèi)容:文章來源地址http://www.zghlxwxcb.cn/news/detail-705926.html
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;
到了這里,關(guān)于Web學(xué)習(xí)筆記-React(路由)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!