单页面应用程序
- SPA: Single Page Application 单页面应用程序,整个应用只有一个页面(index.html)
- MPA: Multiple Page Application 多页面应用程序,整个应用中有很多页面(*.html)
SPA优势
- 更快的页面加载速度: 在SPA中,大部分资源(HTML、CSS、Scripts)仅在网站加载时请求一次。之后的页面更新通过Ajax请求获取必要的数据,仅替换页面的部分内容而不是加载整个新页面。这减少了页面加载时间,提供了更快的用户体验。
- 更流畅的用户体验: SPA可以实现无刷新切换页面,这意味着应用可以像桌面应用那样流畅。用户不需要等待页面重新加载,从而提高了交互性和用户满意度。
- 前后端分离: SPA允许开发者在前端和后端之间建立清晰的界限。后端专注于API开发,而前端则处理用户界面和用户体验,这种模式便于团队协作和应用维护。
- 更好的移动端适应性: 由于SPA在设计时就考虑到了动态内容更新,因此它们通常可以更容易地适应不同的屏幕尺寸和设备。这对于创建响应式设计和跨平台应用尤其有利。
- 简化的调试过程: 使用如Chrome的开发者工具,SPA的调试过程可以更加直接和高效。开发者可以轻松查看网络操作、调查页面元素和数据绑定。
- 减少服务器负载: SPA在用户与应用交互过程中减少了对服务器的请求。由于大部分页面逻辑都在客户端完成,因此可以减轻服务器负载。
SPA缺点
不利于SEO优化(搜索引擎优化)
- 搜索引擎爬虫只会进行HTML页面的文本内容,不会执行JS代码
如何解决这个问题?
使用SSR(服务端渲染)来解决SEO问题,直接给浏览器渲染一个纯HTML的内容,或者使用页面静态化。
React路由介绍
使用单个页面管理更多的页面,进行开发SPA应用
- 前端路由的功能:让用户从一个页面导航到另外一个页面
- 页面路由是一套映射规则,在React中,是URL路径和组件的对应关系
- 使用React路由简单来说就是:配置路径和组件
手动实现单页面应用程序
1.创建三个字组件分别是My.js Friend.js Home.js,然后调用componentDidMount这个钩子函数,在改变锚节点的时候获取到相应的链接,在渲染的时候用JSP进行判断即可,详细请看下面代码(React18写法):
import React, { Component } from 'react';
import { createRoot } from 'react-dom/client'; // Import createRoot
import Home from "./pages/Home";
import Friend from "./pages/Friend";
import My from "./pages/My";
class Index extends Component {
state = {
currentHash: ''
};
componentDidMount() {
window.addEventListener('hashchange', () => {
this.setState({
currentHash: window.location.hash.slice(1)
});
});
}
render() {
const { currentHash } = this.state;
return (
<div>
<h1>组件</h1>
<ul>
<li><a href="#/home">home</a></li>
<li><a href="#/my">my music</a></li>
<li><a href="#/friend">my Friend</a></li>
</ul>
{currentHash === '/home' ? <Home /> : null}
{currentHash === '/my' ? <My /> : null}
{currentHash === '/friend' ? <Friend /> : null}
</div>
);
}
}
// Use createRoot to render the application
const container = document.getElementById('root');
const root = createRoot(container); // Create a root.
root.render(<Index />); // Initial render
React-router-dom
React-router-dom目前有两个版本一个版本是v6版本一个是V5版本二者存在语法上的差异,我的这个版本是以V6版本为例
安装
install add react-router-dom
导入核心组件
import {HashRouter, Route, Link} from 'react-router-dom'
在Index页面中如何导入三个组件
import React, { Component } from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import Friend from './pages/Friend';
import My from './pages/My';
class Index extends Component {
state = {
currentHash: window.location.hash.slice(1)
};
componentDidMount() {
window.addEventListener('hashchange', this.handleHashChange);
}
componentWillUnmount() {
window.removeEventListener('hashchange', this.handleHashChange);
}
handleHashChange = () => {
this.setState({
currentHash: window.location.hash.slice(1)
});
};
render() {
return (
<HashRouter>
<div>
<h1>组件</h1>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/my">我的音乐</Link>
</li>
<li>
<Link to="/friend">我的盆友</Link>
</li>
</ul>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/my" element={<My />} />
<Route path="/friend" element={<Friend />} />
</Routes>
</div>
</HashRouter>
);
}
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Index />);
使用HashRouter进行包裹,并且使用Routes进行点击路由显示具体组件的内容包裹,替换原本的判断路由使用Route,里面传递两个属性,Path具体的路由路径,element渲染节点组件
注意:一个项目中只有一个HashRouter
Router详细说明
在React中Router有两种形式
- HashRouter:使用URL哈希实现,监听hashchange事件来进行实现(http://localhost/3000/#/page)
- BrowerRouter:使用h5的哈希API实现,监听popstart事件进行实现(http://localhost/3000/page)
<Route path="/" element={<Home />}></Route>
<Route path="/my" element={<My />} />
注意:这时候路由会匹配到“/”只要有出现“/”开头路由都会进行匹配
精确匹配
<Route exact path="/" element={<Home />}></Route>
exact属性可以进行路由的精确匹配,必须符合path要求否则不显示组件
Routes组件
处理,当遇到没有发现路由匹配的地址时,出现404组件,可以进行自定义404页面
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/my" element={<My />} />
<Route path="/friend" element={<Friend />} />
<Route path="*" element={<Page404 />} />
</Routes>
路由执行的过程
1.点击Link组件,修改了浏览器地址栏中的url
2.React路由间听到地址栏url的变化
3.React路由内部便利所有的Route组件,使用路由规则(path)与 pathname(hash)进行匹配
4.当路由规则(path)能够匹配地址栏中的pathname(hash)时,就该展示Route组件的内容
嵌套路由
我想在my中渲染两个路由,一个是我的主页,一个是我的音乐
import React, { Component } from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import Friend from './pages/Friend';
import My from './pages/My'; // Assuming My is correctly imported
import Page404 from './pages/Page404';
import Music from './pages/music'; // Correct if file is named 'music.js'
import Myself from './pages/Myself';
class Index extends Component {
render() {
return (
<HashRouter>
<div>
<h1>组件</h1>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/my">我的音乐</Link>
</li>
<li>
<Link to="/friend">我的盆友</Link>
</li>
</ul>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/my" element={<My />}>
<Route path="mymusic" element={<Music />} />
<Route path="meself" element={<Myself />} />
</Route>
<Route path="/friend" element={<Friend />} />
<Route path="*" element={<Page404 />} />
</Routes>
</div>
</HashRouter>
);
}
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Index />);
主函数中使用Route进行缩进
import React, {Component} from 'react';
import {Link, Outlet} from "react-router-dom";
class My extends Component {
render() {
return (
<div>
我是My组件
<ul>
<li>
<Link to="/my/mymusic">我的音乐</Link>
</li>
<li>
<Link to="/my/meself">我的主页</Link>
</li>
</ul>
<Outlet/>
</div>
);
}
}
export default My;
my组件中使用Link进行引导组件,并且在to属性中必须携带父组件地址,使用Outlet进行组件进行遍历
Navigate重定向
import React, { Component } from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter, Routes, Route, Link,Navigate} from 'react-router-dom';
import Home from './pages/Home';
import Friend from './pages/Friend';
import My from './pages/My'; // Assuming My is correctly imported
import Page404 from './pages/Page404';
import Music from './pages/music'; // Correct if file is named 'music.js'
import Myself from './pages/Myself';
class Index extends Component {
render() {
return (
<HashRouter>
<div>
<h1>组件</h1>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/my">我的音乐</Link>
</li>
<li>
<Link to="/friend">我的盆友</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<Navigate to="/home" />} />
<Route path="/home" element={<Home />} />
<Route path="/my" element={<My />}>
<Route path="mymusic" element={<Music />} />
<Route path="meself" element={<Myself />} />
</Route>
<Route path="/friend" element={<Friend />} />
<Route path="*" element={<Page404 />} />
</Routes>
</div>
</HashRouter>
);
}
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Index />);
在代码28行可以看到我的给首页设置了个重定向,到home,使用组件Navigate
编程式导航
通过编程的方法进行导航,例如点击按钮登陆,登陆后跳转到后台首页
import React from 'react';
import { useNavigate } from 'react-router-dom';
function Friend() {
const navigate = useNavigate();
const handleClick = () => {
console.log('登陆成功');
navigate('/home'); // Navigate to the homepage
};
return (
<div>
我是Friend组件
<button onClick={handleClick}>登陆</button>
</div>
);
}
export default Friend;
使用useNavigate进行路由跳转,useNavigate 是 React Router v6 中提供的一个 hook,它用于在函数组件中进行编程式导航,即在不通过 组件或 history 对象的情况下,在代码中手动执行导航操作。
动态路由和动态路由参数的获取
import React, { Component } from 'react';
import { createRoot } from 'react-dom/client';
import { Routes, BrowserRouter, Route, Link } from "react-router-dom";
import Detailed from "./pages/detailed";
class Index extends Component {
render() {
return (
<BrowserRouter>
<div>
详情页
<ul>
<li><Link to="/detailed/1">商品1</Link></li>
<li><Link to="/detailed/2">商品2</Link></li>
<li><Link to="/detailed/3">商品3</Link></li>
</ul>
<Routes>
<Route path="/detailed/:id" element={<Detailed />} />
</Routes>
</div>
</BrowserRouter>
);
}
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<Index />);
在原来的路由匹配把第二个参数改为“:id”的形式进行参数传递
import React from 'react';
import { useParams } from 'react-router-dom';
const DetailComponent = () => {
const { id } = useParams();
return (
<div>
<h2>Detail Component</h2>
<p>ID: {id}</p>
</div>
);
}
export default DetailComponent;
useParams 返回一个对象,其中包含了所有与当前路由匹配的参数。在这个例子里,这个对象将有一个名为 Id 的属性,其值为当前URL中对应位置的实际字符串。
原文链接:https://juejin.cn/post/7343206380696387610 作者:半世浮生