SPA理解

SPA (Single Page Application)

基本使用

在React中,路由通常使用react-router-dom库来实现。以下是一个基本的路由组件使用示例。

1.安装依赖:

1
npm install react-router-dom

2.创建路由组件:在项目中创建一个路由组件,例如App.js

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
import React from 'react'
import {BrowserRouter,Route,Routes,Link} from 'react-router-dom'

//定义一个简单组件
const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const Contact = () => <h2>Contact</h2>;

function App(){
return (
<BrowserRouter>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>

<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
)
}

BrowserRouter:用于包裹整个应用,提供路由功能

Route:定义路由路径和对应的组件

Routes:包裹所有的Route组件,用于匹配当前URL并渲染对应的组件

Link用于在应用中导航,不会导致页面刷新

一般组件与路由组件

功能上

一般组件:普通React组件,用于构建应用的UI部分。在不同的页面或视图中复用

路由组件:根据URL路径渲染特定的页面或视图,定义不同 的部分,每个部分对应一个URL。实现单页面应用(SPA)。提供导航和页面切换功能

定义上

一般组件:使用函数组件或者类组件,不依赖于URL,可以传递props来定制组件行为

路由组件:使用函数组件或类组件。通过Route组件定义路径,并在Routes管理。可以使用useParams、useLocation等Hook来获取路由信息

代码组织

一般组件:通常存放在components文件夹中

src/

├── components/

│ ├── Button.js

│ ├── Card.js

│ └── Header.js

└── App.js

路由组件:通常存放在pages文件夹中

src/

├── pages/

│ ├── Home.js

│ ├── About.js

│ ├── Contact.js

│ └── UserProfile.js

├── components/

│ ├── Button.js

│ ├── Card.js

│ └── Header.js

└── App.js

1
<NavLink className="list-group"></NavLink>

NavLink与Link不同的是可以根据是否匹配链接的路径来应用不同的样式或行为。常用于高亮当前激活的导航链接

react-router-dom v5老版本中,如果不指定activeClassName来指定属性,通常使用active属性来决定高亮,现在v6版本直接使用className和style来自定义样式

1
2
3
4
5
<NavLink  to="/" className={({ isActive }) => (isActive ? 'active' : 'inactive')}
style={({ isActive }) => ({
fontWeight: isActive ? 'bold' : 'normal',
color: isActive ? 'red' : 'blue',
})}> Home </NavLink>

Routes

在老版本v5中是Switch组件,在新版v6版本中是Routes替代了。它会自动选择与当前URL最匹配的路由,并且支持嵌套路由,并且路径匹配是按顺序进行的。

介绍一下用法:

1.基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Dashboard from './components/Dashboard';
import NotFound from './components/NotFound';

function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
{/* 匹配不到时显示404 */}
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
}

export default App;

2.使用element属性

Route组件使用element属性来指定要渲染的组件,而不是之前的component属性

1
<Route path="/about" element={<About />} />

3.精确匹配

在老版本v5中,路径匹配有模糊匹配的情况出现,例如下面

1
2
3
4
5
6
7
<BrowserRouter>
<Link to="/home/a">Home</Link>

<Switch>
<Route path="/home" element={<Home/>}/>
</Switch/>
</BrowserRouter>

Link组件的to属性多了一个’a’路径后缀,但是在Route组件中path属性没有,但是仍能匹配上,这就是模糊匹配。如果要精确匹配,需要将path改成exact path

但是在v6版本中,默认匹配是精确的,即直接使用path即可,不需要显示设置exact属性

1
2
3
4
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>

react-route-dom v5版本中,使用Redirect组件将用户从一个路径重定向到另一个路径

1
2
3
4
5
6
7
8
9
<Router>
<Switch>
<Route path="/home" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/old-page" component={OldPage} />
<Redirect from="/old-page" to="/about" />
<Redirect to="/404" />
</Switch>
</Router>

当访问/old-page会自动重定向到/about中,当访问都匹配不上时,重定向到/404页面中

react-routr-dom v6版本中,使用Navigate组件实现重定向

1
2
3
4
5
6
7
8
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/old-page" element={<Navigate to="/about" replace />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Router>

嵌套路由

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
38
39
40
41
42
43
44
45
46
47
48
49
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Link, Navigate } from 'react-router-dom';

// 父组件
const Dashboard = () => (
<div>
<h2>Dashboard</h2>
<nav>
<ul>
<li><Link to="profile">Profile</Link></li>
<li><Link to="settings">Settings</Link></li>
</ul>
</nav>
<Routes>
<Route index element={<DashboardHome />} />
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
<Route path="*" element={<Navigate to="profile" replace />} />
</Routes>
</div>
);

// 子组件
const DashboardHome = () => <h3>Welcome to your Dashboard</h3>;
const Profile = () => <h3>Profile Page</h3>;
const Settings = () => <h3>Settings Page</h3>;

function App() {
return (
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/dashboard">Dashboard</Link></li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard/*" element={<Dashboard />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Router>
);
}

// 其他组件
const Home = () => <h2>Home</h2>;

export default App;

向路由组件传递参数

1.params参数

路由链接需要携带参数

1
<Link to='demo/test/tom/18'> </Link>

注册路由需要声明接收

1
<Route path="demo/test/:name/:age" component={Test}>

接收参数

1
const {name, age} = this.props.match.params

2.search参数

携带参数

1
<Link to={`/home/?id=${msgObj.id}&title=${msgObj.title}`}></Link>

search参数无需声明接收

接收参数

1
2
const {search} = this.props.location
const res = qs.parse(search.slice(1))

使用 import qs from 'qs'用于将对象与urlencoded相互转换

user = {name: 'Tom', age: 18}

s = qs.stringify(user)

3.state参数

1
<Link to={{pathname:'/home/message/detail',state:{id: msgObj.id, title: msgObj.title}}}></Link>

state参数无需声明接收