react-router
一、在react项目中安装路由
官方文档:https://reactrouter.com/en/v6.3.0/getting-started/installation#basic-installation
npm
$ npm install react-router-dom@6
1、选择路由模式:在入口文件index.js中
-
选择路由模式:BrowserRouter 历史记录路由模式;HashRouter 哈希路由模式
-
<BrowserRouter>
<app/>
</BrowserRouter> 路由模式包裹根组件
import React from "react"; // 引入react => 获取到react提供的api import ReactDOM from "react-dom/client"; // 将虚拟dom => 真实dom import "./index.css"; import App from "./App"; //引入根组件 import { BrowserRouter,HashRouter } from "react-router-dom"; // BrowserRouter => 选择路由模式 => history const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <BrowserRouter> {/* 这个里面的组件 如果要使用路由 就会采用history => 本质 provider => 在父组件中提供数据,在自己组件就是可以使用 react-router-dom 提供的api 提供的那些东西 => 路由模式, 路由api */} {/* react => 组件渲染的结构 => 父子结构 */} <App /> </BrowserRouter> );
三、创建路由表:写组件和路径对应关系
-
在选择路由模式后,通过 react-router-dom提供的api 来实现,可以使用全局组件:
-
Routes 路由表 2. Route 路由信息 3. Link 相当于a标签 可以跳转路由 4. OutLet 路由占位符
-
-
路由模块化:配置路由表
-
在sr目录下创建一个文件夹=》router/index.js文件 =》创建路由表=》就是一个组件
-
有几个属性:path 写路径 element写组件
-
<Routes>
<Route path="/login" element={<Login></Login>}></Route>
</Routes>
-
在到根组件app.js中进行引入 =》使用这个组件
-
-
-
在按个路由组件进行嵌套 => 直接添加路由信息组件
-
嵌套路径 的路径会进行拼接
-
根据路径匹配嵌套路由 => 路由占位符 <OutLet> </OutLet>=> 在哪里展示这个嵌套路由就需要在哪里写路由占位符
import { Routes, Route } from "react-router-dom";
// Routes =》 路由表
// Route => 路由信息
import { useNavigate } from "react-router-dom";
// 从首页跳转到login
// 语法: useNavigate() =》 返回值也是一个方法,这个方法有两个参数
// Link => 相当于A标签
// let routes =[
// {path:'/',component:'组件'}
// ]
// 四 创建路由表
// 通过react-router-dom提供的api 来实现
// 有几个属性 => path element
// 五 链式的路由跳转
// 方式 push , replace , go
// 1 push :浏览器历史栈中有记录,可以回退
// 2 reaplace: 浏览器历史栈没有记录,不可以回退
// 3 go : 前进和后退
// 八 嵌套路由
// 概念:在页面级组件中展示页面级组件
// 用法: 1 配置嵌套路由
import Index from "../views/Index/Index";
import Login from "../views/Login/Index";
import Me from "../views/Me/Index";
import IndexShow from "../views/IndexShow/Index";
import NotFind from "../views/NotFind/Index";
function RouterList() {
return (
<Routes>
<Route path="/" element={<Index></Index>}>
{/*
嵌套路由
1. 在按个路由组件进行嵌套 => 直接添加路由信息组件
2. 嵌套路径 的路径会进行拼接
3. 根据路径匹配嵌套路由 => 路由占位符 => 在哪里展示这个嵌套路由就需要在哪里写路由占位符
*/}
<Route path="indexshow" element={<IndexShow></IndexShow>}></Route>
</Route>
<Route path="/login" element={<Login></Login>}></Route>
<Route path="/Me" element={<Me></Me>}></Route>
<Route path="*" element={<NotFind></NotFind>}></Route>
</Routes>
);
}
export default RouterList;
return( <div> index <button onClick={()=>goPath()}>路由传递参数 可见</button> <button onClick={()=>goPath2()}>路由传递参数 不可见2</button> <Outlet></Outlet> </div> )
作用:用户没有进入到这个页面,这个页面的代码就不会在首次加载项目的时候,加载处理,提供项目首次加载的速度
语法:React.lazy() 结合 Suspense
React.lazy() 语法 =》 React.lazy(()=>import('组件路径')) =》路由懒加载组件
Suspense 语法=》 < Suspense fallback ={先展示的组件(异步组件)} ></Suspense>
Suspense不能直接包围整个路由表,否则会出现页面创建两次的bug
哪些需要展示异步组件,路由懒加载的就包裹哪些组件即可
/* 十二: 路由懒加载 作用:用户没有进入到这个页面,这个页面的代码就不会在首次加载项目的时候,加载处理,提供项目首次加载的速度 // 语法 :React.lazy() 结合Suspense React.lazy() 语法 => React.lazy(()=>import('组件路径')) => 路由懒加载组件 Suspense 语法=> <Suspense fallback={先展示的组件}></Suspense> Suspense */ import { Routes, Route,Link } from "react-router-dom"; import React,{Suspense} from "react"; /* 路由懒加载 + 异步组件 React.lazy(()=>{}) => 返回值 就是 我们的路由懒加载数据 // Suspense =》 结合懒加载使用,当路由懒加载组件还没有显示,先加载Suspense 提供组件 */ // 写一个公共的方法 处理懒加载组件=》他的返回值就是懒加载组件 import Index from "../views/Index/Index"; import Login from "../views/Login/Index"; // import Me from "../views/Me/Index"; // import IndexShow from "../views/IndexShow/Index"; import NotFind from "../views/NotFind/Index"; // 封装路由懒加载的方法 function ChangeLazyC(name){ let Com Com = React.lazy(()=>import(`../views/${name}/Index`)) //返回懒加载组件 return <Com></Com> } function RouterList() { return ( <Suspense fallback={<div>loading...</div>}> <Routes> <Route path="/" element={<Index></Index>}> <Route path="indexshow" element={ChangeLazyC('IndexShow')}></Route> </Route> <Route path="/login" element={<Login></Login>}></Route> <Route path="/Me" element={ChangeLazyC('Me')}></Route> <Route path="*" element={<NotFind></NotFind>}></Route> </Routes> </Suspense> ); } export default RouterList;
优化封装 自动处理成 懒加载组
//写一个公共的方式 处理懒加载组件 =>他的返回值就是懒加载组件 function ChangeLazyC(name){ let Com Com = React.lazy(()=>import(`../views/${name}/index`)) //返回懒加载组件 return <Com></Com> }
4、在根组件App.js中引入路由表
-
在根组件中引入路由表
import './App.css'; import { Link } from 'react-router-dom'; // 引入路由表 import RouterList from './router/index' import useRedirect from './useHooks/routerRedirect'; import useBeforEach from './useHooks/routerBeforEach'; import useRouteReEa from './useHooks/routerReEa'; // 创建路由表 => 全局组件 => react-router-dom // Link => 相当于A标签 // let routes =[ // {path:'/',component:'组件'} // ] function App() { // useRedirect(); // 路由重定向 // useBeforEach(); // 路由守卫 useRouteReEa(); // 路由重定向 路由守卫 合并 return ( <div className="App"> <div> <Link to="/">首页</Link> | <Link to='/login'>登录</Link> </div> <RouterList></RouterList> // 引入路由表 </div> ); } export default App;
四、链式的路由跳转
-
通过 react-router-dom提供的api : useNavigate()导航
-
语法:let navigate = useNavigate(); 返回值也是一个方法navigate,这个方法有两个参数,参数一:路由,参数二:配置项{ },改变跳转方式 和 路由传递参数
-
navigate('/跳转路由',{跳转方式:true})
-
-
push:语法:navigate('/跳转路由'); 浏览器历史栈中有记录,可以回退;它是默认跳转方式;
-
reaplace:语法:navigate('/路由', {replace:true}); 浏览器历史栈中没有记录,不可以回退
-
go: 语法:navigate(1) 正数前进 ; navigate(-1) 负数后退
import {useNavigate,Outlet} from 'react-router-dom'
// 从首页跳转到 login
// 语法: useNavigate() => 返回值也是一个方法,这个方法有两个参数
// 参数一:路由
// 参数二:配置项 => {} => 改变跳转方式 和 路由传递参数
// function Index(){
// let navigate=useNavigate(); // 路由跳转的方式useRouter
// // console.log(navigate)
// const goLogin=()=>{
// navigate('/login'); // 默认是push 跳转方式
// }
// const goLoginR=()=>{
// navigate('/login',{replace:true}); // replace
// }
// const goLoginG=()=>{
// navigate(1)
// }
// return(
// <div>
// <h2>这里是首页哦!</h2>
// <button onClick={()=>{goLogin()}}>去登录 push</button>
// <button onClick={()=>{goLoginR()}}>去登陆 replace</button>
// <button onClick={()=>{goLoginG()}}>Go/Back go</button>
// </div>
// )
// }
// export default Index
// /*
// 总结 路由跳转的方式
// 1 push => 默认的
// 2 replace => 需要自己进行配置 => useNavigate()
// 3 go => useNavigate() => 返回方法 它的参数写数字(正数前进,负数后退)
// */
五、路由传递参数
-
路由参数在路由地址上可见
-
传递路由参数: 字符串模板拼接(最多使用) navigate(
/login?id${要传递的参数} && ids=300) -
获取路由参数:通过 react-router-dom提供的api : useLocation() 1. useLocatinos(获取当前url上的信息); // 相当于vue-router中的useRoute路由信息 2. 语法:let location = useLocation(); 1. 通过location.search; 获取到的路由参数是一个字符串 2. 利用插件利用插件query-string 解析url上面的参数
-
路由参数在路由地址上不可见
-
传递路由参数: 1. 语法:navigate(
/login,{state:{id:100}); // 通过state传参不可见 -
路由传参:
// 路由传递参数 function Index(){ let navigate=useNavigate(); // 1 可见 // 字符模板拼接 最多 const goPath=()=>{ let id=100; navigate(`/login?id=${100}&&ids=300`) } // 不可见 const goPath2=()=>{ navigate(`/me`,{ state:{id:1100} }) } return( <div> index <h2>这里是首页哦!</h2> <button onClick={()=>goPath()}>路由传递参数,可见</button> <button onClick={()=>goPath2()}>路由传递参数,不可见2</button> <Outlet></Outlet> </div> ); } export default Index;
获取到路由参数
import { useNavigate, useLocation,Outlet } from "react-router-dom";
import qs from "query-string";
// 获取到路由信息=》 路由参数可见 => 解析过来的参数是一个字符串
// 我们需要将这个字符串变成 =》 对象的形式 => 第三方库 query-string
// 1 项目中安装 =》 npm install query-string
// 2 在那里需要解析url 参数的地方引入 import qs from 'query-string'
// 3 使用它里面的一个方式qs.parse(需要解析的url地址)
// useLocation => 相 当于vue-router useRoute
// 语法 =》 let location = useLocation()
// 获取到路由参数
// 1) 获取可见的路由参数
function Login() {
let navigate = useNavigate();
let location = useLocation();
// 如果路由参数可以见 =》 获取到的路由参数是一个字符串 ?id=100&&ids=300 =》{id:100,ids:300}
// 解决方法 -=》 通过第三方的一个库 query-string => 解决 url上面的参数
console.log(location);
console.log(qs.parse(location.search));
const goPath = () => {
navigate("/me");
};
const logins=()=>{
alert('登录成功');
sessionStorage.setItem('token','11111')
}
// 登录
return (
<div>
<h2>这里是登录页哦!</h2>
<button onClick={() => goPath()}>去个人页面 me</button>
<button onClick={()=>{logins()}}>登录</button>
</div>
);
}
export default Login;
六、路由重定向
-
需要自己定义方法来处理:自定义hooks
-
概念:
-
就是react 没有给我提供这个 hooks,我自己定义的方法,是按照一定的规则
-
以use 声明的方法
-
可以是 react 提供的hooks
-
-
案例:实现路由重定向 => 当我访问 首页的路径 => 重定向到首页展示路径
-
操作步骤:
-
在src目录下定义一个useHooks 存放自定义的hooks
-
再创建一个routerRedirect.js文件
-
在app文件中引入这个自定义的Hooks,直接调用
-
import {useLocation,useNavigate} from 'react-router-dom'
import {useEffect} from 'react'
function useRedirect(){
// 实例路由重定向 =》 当我访问 首页的路径 =》重定向到首页展示路径
// 1 知道当前的路径
let location = useLocation()
// 2 在路由跳转
let navigate = useNavigate()
//监听当前的路由信息
useEffect(()=>{
if(location.pathname=='/'){
navigate('/indexshow',{replace:true})
}
},[location.pathname])
}
export default useRedirect
在App.js根组件直接使用:useRedirect() //自定义hooks,实现路由重定向
import RouterList from './router/index' import useBeforEach from './useHooks/routerBeforEach' import useRedirect from './useHooks/routerRedirect' function App() { useRedirect() //自定义hooks,实现路由重定向 useBeforEach() //自定义hooks,实现路由守卫 return ( <div className="App"> <RouterList></RouterList> </div> ); } export default App;
七、路由守卫
1. 在react中=》我定义自定义hooks
-
-
案例:判断本地存储中是否有"token", 如果没有就跳转到登录页面
-
操作步骤
-
在src目录下定义一个useHooks 存放自定义的hooks
-
再创建一个routerRedirect.js文件
-
在app文件中引入这个自定义的Hooks,直接调用
-
import { useLocation, useNavigate } from "react-router-dom";
import { useEffect } from "react";
function useBeforEach() {
//在react中=》我定义自定义hooks=>
//处理内部页面和外部页面
let location = useLocation();
let navigate = useNavigate();
useEffect(() => {
console.log(location);
if (location.pathname != "/login") {
//内部页面
//判断用户是否登录
if (!sessionStorage.getItem("token")) {
// 没有登录
//去登录页面
navigate("/login", { replace: true });
}
}
}, [location.pathname]);
}
export default useBeforEach;
发现这个路由守卫和路由重定向 都需要监听路由信息 =》进行合拼
import { useLocation, useNavigate } from "react-router-dom";
import { useEffect } from "react";
//这个自定义hooks => 1 实现路由重定向 2实现路由守卫=》判断内部页面和外部页面
function useRouterReEa() {
let location = useLocation();
let navigate = useNavigate();
useEffect(() => {
if (location.pathname != "/login") {
//内部页面
//判断用户是否登录
if (!sessionStorage.getItem("token")) {
// 没有登录
//去登录页面
navigate("/login", { replace: true });
} else {
//登录了
if (location.pathname == "/") {
navigate("/indexshow", { replace: true });
}
}
}
}, [location.pathname]);
}
export default useRouterReEa;
八、处理404页面
<Route path='*' element={<NotFind ></NotFind >}></Route>
九、动态路由
function ListItem(list){ if(list.length>0){ return list.map((item,index)=>{ return <Route key={index} path="bashbord" element={LazyC('DashBord')}></Route> }) } }
-
-
点击 Link 组件(a 标签)时,会修改浏览器url地址栏中的pathname
-
路由监听到 url 地址变化之后,得到最新的 pathname,再遍历所有的 Route 组件。使用 pathname和 Route 中的 path(路由规则) 进行比对,找到匹配的 Route
-
当路由规则(path)能够匹配地址栏中的 pathname 时,就展示该Route 组件的内容
-
-
总结:
十、路由优化
-
react执行机制:只要路由地址发生改变,路由表就会重新执行(所以路由重定向/导航组件不能发在App根组件中,而要放在Layout组件中)
-
react 路由组件执行流程 => 去到内部页面,需要在执行路由表
-
总结:根据后端给的数据动态生成 =》 2 点击登录获取权限 =》 去到内部页面 =》需要重新执行路由表,只能执行一次
-
Suspense不能直接包围整个路由表,否则会出现页面创建两次的bug
哪些需要展示异步组件,路由懒加载的就包裹哪些组件即可
用法见下面代码:
import { Outlet } from "react-router-dom";
import "./layout.css";
import useRouterReEa from "../useHooks";
function Layout() {
// react执行机制:只要路由地址发生改变,路由表就会重新执行(所以路由重定向/导航组件不能发在App根组件中,而要放在Layout组件中)
useRouterReEa();
// 这个侧边栏导航的数据需要动态生成
// 权限: 1 基础版本 => 前端 => 内部页面和外部页面 =》 路由守卫 后端:内部接口和外部接口 官网
// 2 进阶版本 =》 后台管理系统 =》 页面 =》 1 侧边栏导航 2 动态路由
// 3 中级版本 =》 后台管理系统 =》 自己可以设置权限
// 这个侧边栏导航的数据需要的动态生成 =》 根据用户登录的时候获取
let navList = ["dashBord","order","Index"];
return (
<div>
<div className="header">内部管理系统</div>
<div className="layout">
<div className="leftNav">
{
navList.map((item,index)=>{
return (
<div key={index}>{item}</div>
)
})
}
</div>
<div className="rightNav">
<Suspense fallback={<div>loading...</div>}>
<Outlet></Outlet>
</Suspense>
</div>
</div>
</div>
);
}
export default Layout;
router/index.js文件
import { Routes, Route } from "react-router-dom";
import React, { Suspense } from "react";
import Layout from "../Layout/layout";
import Login from "../views/Login/index";
// 路由懒加载
function LazyC(name) {
let Com;
Com = React.lazy(() => import(`../views/${name}/index`));
return <Com></Com>;
}
function RouterList() {
console.log('路由表');
// 需要获取到根据权限登录的时候后端返回的数据
// 因为登录页面 =》 用户第一次进入需要加载页面 =》['dashBord','Index','Play']
let navList=['dashBord','Index','Play']
return (
<Suspense fallback={<div>loading...</div>}> // 把这里注释掉,放到包裹到具体的组件上
<Routes>
<Route path="/login" element={<Login></Login>}></Route>
<Route path="/layout" element={<Layout></Layout>}>
{/* /layout/bashbord */}
{ListItem(navList)}
</Route>
</Routes>
</Suspense> // 把这里注释掉,放到包裹到具体的组件上
);
}
function ListItem(list){
if(list.length>0){
return list.map((item,index)=>{
return(<Route key={index} path="dashbord" element={LazyC("DashBord")}></Route>)
})
}
}
// 2 react 路由组件执行流程 =》 去到内部页面,需要在执行路由表 =》 redux(项目中讲)
// 总结 =》
// 1 根据后端给的数据动态生成 =》 2 点击登录获取权限 =》 去到内部页面 =》需要重新执行路由表,只能执行一次
export default RouterList;
浙公网安备 33010602011771号