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;