前端路由指的是一种将浏览器URL与特定页面或视图关联起来的技术。在传统的Web开发中,当用户点击链接或者输入URL时,服务器会接收到请求并返回相应的HTML页面。而在前端路由中,当用户点击链接或者输入URL时,浏览器会根据路由规则对URL进行解析,并使用JavaScript控制页面的展示。
前端路由通常使用JavaScript库来实现,比如React Router、Vue Router等。它们允许开发者定义路由规则,并根据这些规则来显示不同的组件或页面
前端路由可以提高Web应用的性能和用户体验,因为它允许应用实现快速的页面切换和动态的内容加载,同时减少了服务器的负载
安装
官网:https://reactrouter.com/home
ReactRouter包含三个内容:(1) react-router:核心库;(2) react-router-dom:正常PC用的;(3) react-router-native:移动native用的
当前使用版本:"react-router-dom": "^6.30.0"
安装:npm install react-router-dom@6
路由模式
React的路由需要在某个模式下包裹使用,不能单独使用
HashRouter(哈希路由):类似a标签锚点,在本页跳转,所以拿不到历史记录,因为没有跳出当前页面
http://localhost:3000/#/home
History(在React中叫BrowserRouter,历史记录模式):模拟历史记录模式,可以有前进后退的历史记录
http://localhost:3000/home
//刷新页面,<BrowserRouter>会将当前路由发送到服务器
//需要后端配合就是当收到请求的url不是功能性的,而是前端路由时,重新加载入口html文件
路由组件和属性 (Link、NavLink、Outlet、Routes、Navigate、element)
Link:负责跳转
NavLink:将包裹的内容渲染为a标签,并给Link加上一个样式active类,设置类的样式达到激活菜单的效果
Outlet:相当于一个占位符,目的就是为了用来占位展示当前组件对应的Home1和Home2(类似于vue中的router-view)
Routes:路由拦截并展示对应的组件
<Routes>
<Route path={"/home"} element={<Home/>}/>
<Route path={"/about"} element={<About/>}/>
</Routes>
element:表示对应组件
Navigate:相对于重定向
路由嵌套的时候:子组件的path不需要写斜杠"/",直接写就好
<Route path={"/home"} element={<Home/>}>
<Route path="home1" element={<Home1/>}/>
<Route path="home2" element={<Home2/>}/>
</Route>
默认展示组件:
<Route index element={<Home1/>}/> <!--index表示默认要展示的组件,去掉path-->
src\App.js
import './App.scss';
import {
HashRouter, //-----路由容器,装路由组件
// Link, //-----跳转
Routes, //-----路由拦截展示的容器
Route, //-----拦截路径,设置展示组件
Navigate, //-----类似重定向
NavLink, //-----跳转,带active类,但需要自己写样式
// BrowserRouter //-----路由容器,装路由组件
//Outlet //-----占位符,用来展示嵌套路由的子组件的内容
} from 'react-router-dom';
import Home from "./Home";
import About from "./About";
import Home1 from "./Hom1";
import Home2 from "./Home2";
// 用户乱输入地址栏url,则返回404页面组件
const Err=()=><div>Error错误404页面</div>
//React-Router案例
function App() {
return (
<>
{/*<BrowserRouter>*/}
<HashRouter future={{
v7_startTransition: true,
v7_relativeSplatPath: true
/*解决:使用react-router-dom@6.30.0版本时,组件默认打印未来版本的警告信息,影响项目代码功能调试*/
}}>
{/*<Link to={"/home"}>Home</Link>*/} {/*你要去哪里*/}
<NavLink to={"/home"}>Home</NavLink>
{/*<Link to={"/about"}>About</Link>*/}
{/*<NavLink to={"/about/月亮/25"}>About</NavLink>*/} {/*路由传参*/}
<NavLink to={"/about?a=1&b=2"}>About</NavLink>
<Routes> {/*拦截并展示对应的组件*/}
<Route path="/" element={<Navigate to={"/home"}/>}/> {/*通用拦截*/}
<Route path={"/home"} element={<Home/>}>
{/*<Route path={"home1"} element={<Home1/>}/> /!*嵌套子路由*!/*/}
<Route index element={<Home1/>}/> {/*index表示默认要展示的组件,去掉path*/}
<Route path={"home1"} element={<Home1/>}/> {/*加上这一行即可,因为上一行没法拦截对应路由,它本身还是要写*/}
<Route path={"home2"} element={<Home2/>}/>
</Route>
{/*<Route path={"/home/"} element={<Navigate to={"/home/home1"}/>}/> /!*默认要展示的组件(自想方法)*!/*/}
{/*<Route path={"/about/:name/:id"} element={<About/>}/>*/} {/*路由接参*/}
<Route path={"/about"} element={<About/>}/>
<Route path={"*"} element={<Err/>}/> {/*错误404页面*/}
</Routes>
</HashRouter>
{/*</BrowserRouter>*/}
</>
);
}
export default App;
import {NavLink, Outlet} from "react-router-dom";
const Home = () => <div>
<h1>Home</h1>
<NavLink to={"/home/home1"}>Home1</NavLink>
<NavLink to={"/home/home2"}>Home2</NavLink>
<Outlet/> {/*占位符,用来展示Home1和Home2内容的*/}
</div>;
export default Home;
路由传参 ( Hook:useParams 、useSearchParams )
import引入的路由都是引入的属性(大写开头);除了属性之外,路由还可以引入方法(所有的方法都是useXxx的格式)
路由传参(useParams):
1. <NavLink to={"/about/25"}>About</NavLink> <!--带参跳转,可以传递多个参数,右斜杠隔开-->
2. <Route path={"/about/:id"} element={<About/>}/> <!--这里会有参数并且是通过id接收-->
3.组件获取
import {
useParams <!--获取路由传参的方法-->
} from 'react-router-dom';
const params=useParams();
console.log(params.id); <!--{}-->
路由传参第二种方式(useSearchParams):
1. <NavLink to={"/about?a=1&b=2"}>About</NavLink> <!--传了两个参数 a,b-->
2. <Route path={"/about"} element={<About/>}/>
3.获取参数
import {
useSearchParams
} from 'react-router-dom';
const [search]=useSearchParams();
console.log(search.get("a")); <!--通过get方法获取指定参数-->
import {
// useParams, //获取路由传参的方法
useSearchParams
} from 'react-router-dom';
const About=()=> {
// const params=useParams();
// console.log(params.name);
const [search,setSearch]=useSearchParams();
console.log(search.get("a"));
console.log(search); //{size:2} 遍历出来
search.forEach((v,i)=>{
console.log(i,v)
})
return (
<h2>About</h2>
)
}
export default About;
路由跳转(Hook:useNavigate)
React跳转(useNavigate):(在Vue中,push() 会产生历史记录,replace() 不会产生历史记录)
import {useNavigate} from "react-router-dom";
const navigate=useNavigate();
navigate("/home/home2"); <!--这种页面跳转相当于添加了一条历史记录-->
navigate("/home/home2",{replace: true}); <!--这种页面跳转相当于替换掉了一条历史记录-->
navigate(-1) <!--返回 -1:上一页,0:当前页,1:下一页-->
import {useNavigate} from "react-router-dom";
const Home1=()=>{
const navigate=useNavigate();
const goHome2=()=> {
// navigate("/home/home2"); //这种页面跳转相当于添加了一条历史记录
navigate("/home/home2",{replace: true}); //这种页面跳转相当于替换掉了一条历史记录
}
return (
<div>
<h2>Home1</h2>
<button onClick={goHome2}>跳到Home2</button>
</div>
)
}
export default Home1;
import {useNavigate} from "react-router-dom";
const Home2=()=>{
const navigate=useNavigate();
const goBack=()=>{
navigate(-1)
}
return (
<div>
<h2>Home2</h2>
<button onClick={goBack}>返回</button>
</div>
)
}
export default Home2;
路由的构建
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import {HashRouter} from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<HashRouter>
<App />
</HashRouter>
);
src\App.js
import './App.css';
import Routers from './router'
function App() {
return (
<div className="App">
<Routers/>
</div>
);
}
export default App;
router\index.js
import {
useRoutes,
Navigate,
} from 'react-router-dom'
import Power from "./power"; //进行权限判断处理
import Home from '../pages/home'
import Login from '../pages/login'
import Admin from '../pages/home/admin'
import Notice from "../pages/home/notice"
import Student from "../pages/home/student"
//路由配置的组件
const Routers=()=>{
return useRoutes([
{
path:'/',
element:<Navigate to={"/login"}/>
},
{
path:'/login',
element:<Login/>
},
{
path:'/home',
// element:<Home/>,
// element:Power(<Home/>,"/home"), /*高阶函数*/
element:<Power path='/home' ele={<Home/>}/>, /*高阶组件*/
children:[ /*子路由*/
{
index:"index", /*默认展示当前的子组件*/
// path:'admin', /*子路由不需要 /admin 这样写,斜杠不需要*/
element:<Power path='/home/notice' ele={<Notice/>}/>
},
{
path:'notice',
element:<Power path='/home/notice' ele={<Notice/>}/>
},
{
path:'student',
element:<Power path='/home/student' ele={<Student/>}/>
},
{
path:'admin',
element:<Power path='/home/admin' ele={<Admin/>}/>
}
]
}
])
}
export default Routers;
router\power.js 高阶组件
const Power=(props)=>{
console.log("Power执行") //避免多次重复执行,在router中应写作组件<power/>,而不是直接调用Power()
console.log(props)
//这里拦截判断是否可以返回当前组件
//取出sessionStorage中的power比对,如果本地数据中有就返回当前组件,如果本地用户数据中没有就不返回当前组件
let power=JSON.parse(sessionStorage.getItem("power")); //[{},{},{}]
for(let i=0;i<power.length;i++){
if(power[i].link.indexOf(props.path)!==-1){ //有权限访问
return <>
{props.ele}
</>
}
}
return <div><h1>没有权限</h1></div>; //循环完了都没找到有的话,就是没有权限
}
export default Power;
权限拦截:
现在的路由是拦截到请求,直接返回对应的组件,实际上应该先查看用户是否有权限访问
需要写一个函数,接收一个组件为参数。如果有权限,就返回组件;如果没有权限就返回登录或者错误组件
{
path:'/home',
// element:<Home/>, -------以前直接返回组件
element:Power(<Home/>), -------把组件转入Power函数,在函数内部进行逻辑判断,最后根据权限返回需要展示的组件
}
element:Power(<Home/>) -------向一个函数传递一个组件作为参数,我们称为高阶组件,函数负责逻辑代码,组件负责页面展示
高阶组件,源于高阶函数,高阶函数就是把函数A作为参数传给函数B,调用函数B返回一个新的函数C
<script>
function HOF(fn){ //函数B
return function(){ //函数C
fn()
}
}
let myFn=HOF(
()=>{ //函数A
console.log("哈哈哈哈")
}
)
myFn();
</script>
高阶组件HOC,向一个函数A传递一个组件C作为参数,最后返回一个新的组件(使用高阶组件的目的,为了组件的二次加工,或者功能逻辑判断)
项目启动后,路由中的Power函数会被执行