import React from 'react'
import {Navigate, Route,Routes,} from "react-router-dom"
import Film from "../views/Film"
import Cinema from "../views/Cinema"
import Center from "../views/Center"
import NotFound from "../views/NotFound"
export default function Mrouter() {
return (
<div>
<Routes>
{/* 默认路由 */}
{/* }> */}
<Route path="/film" element={<Film />}></Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/center" element={<Center />}></Route>
{/* 匹配到/ 重定向到film页面 */}
<Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
{/* 都没匹配上 跳转到404页面 *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
<Route path="*" element={<NotFound/>}></Route>
</Routes>
</div>
);
}
路由嵌套有两种形式
二级路由不代表路由嵌套,如果组件整个被替换,只是路径上嵌套比如/cinema/search,搜索组件完全替换了cinema组件,而film组件下的两个二级路由是Nowplaying和Comingsoon,这两个组件共用film组件的轮播图
比如说这两个组件,上面部分的轮播图是共用的,只有下面的内容是替换的
那么会将这两个组件放在film组件的哪个位置呢,怎么让它们在轮播图的下面而不是上面,就需要路由容器占位了
相对路径的写法:
像这种组件完全替换,实际上是平级的组件,但是路径是二级路径的代码需要这样写:
import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
return (
<div>
<Routes>
{/* 默认路由 */}
{/* }> */}
<Route path="/film" element={<Film />}>
{/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
{/* }/> */}
{/* 默认路由写法2 重定向路径必须写绝对路径*/}
{/* } /> */ }
{/* 默认路由写法3 */}
<Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
{/* 这样的写法是将这两个组件加载到film组件中来 */}
<Route path="nowplaying" element={<Nowplaying />} />
<Route path="comingsoon" element={<Comingsoon />} />
</Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/cinema/search" element={<Search />}></Route>
<Route path="/center" element={<Center />}></Route>
{/* 匹配到/ 重定向到film页面 */}
<Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
{/* 都没匹配上 跳转到404页面 *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
<Route path="*" element={<NotFound />}></Route>
</Routes>
</div>
);
}
使用NavLink选项卡,选中哪个哪个会自动有class=active,可以给active设置高亮颜色,但是由于很多人一起开发,别人的acive也会影响到我们的颜色,所以要取另外的名,className给NavLink传了个属性,支持回调函数的写法,回调函数接收到一个形参,解构出来isActive
Tabbar.js
import React from 'react'
import {NavLink} from "react-router-dom"
export default function Tabbar() {
return (
<div>
<ul>
<li>
{" "}
<NavLink
to="/film"
className={({ isActive }) => (isActive ? "xxactive" : "")}
>
电影
</NavLink>
</li>
<li>
{" "}
<NavLink
to="/cinema"
className={({ isActive }) => (isActive ? "xxactive" : "")}
>
影院
</NavLink>
</li>
<li>
<NavLink
to="/center"
className={({ isActive }) => (isActive ? "xxactive" : "")}
>
我的
</NavLink>
</li>
</ul>
</div>
);
}
更适合携带参数跳转到另一个页面
使用useNavigate跳转,还有一种方法是this.props.history,但是这种方法有的组件不一定能拿到this.props的值
import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
const [list, setList] = useState([]);
const navigate = useNavigate();
useEffect(() => {
axios(
"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
{
headers: {
"X-Client-Info":
'{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
"X-Host": "mall.film-ticket.film.list",
},
}
).then((res) => {
// console.log(res.data.data.films)
//setState函数是异步的,这样写是同步写法,可以及时获取到List的值
// setList((list)=>{
// list = res.data.data.films;
// console.log(list);
// debugger;
// return list;
// });
// 这样是异步写法,打印的list的值还为空
setList(res.data.data.films);
// console.log(list);
});
}, []);
function handleChangePage(id) {
navigate(`/detail/?id=${id}`);
}
return (
<div>
<ul>
{list.map((item) => (
<li
key={item.filmId}
onClick={() => {
handleChangePage(item.filmId)
}}
>
{item.name}
</li>
))}
</ul>
</div>
);
}
Detail.js
import React from "react";
import { useSearchParams } from "react-router-dom";
export default function Detail() {
const [searchParams, setsearchParams] = useSearchParams();
// console.log(window.location.href);
//拿到上个页面跳转过来传过来的id
console.log(searchParams.get("id"));
return <div>Detail
<button onClick={()=>setsearchParams({'id':1000})}>猜你喜欢</button>
</div>;
}
import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
const [list, setList] = useState([]);
const navigate = useNavigate();
useEffect(() => {
axios(
"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
{
headers: {
"X-Client-Info":
'{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
"X-Host": "mall.film-ticket.film.list",
},
}
).then((res) => {
// console.log(res.data.data.films)
//setState函数是异步的,这样写是同步写法,可以及时获取到List的值
// setList((list)=>{
// list = res.data.data.films;
// console.log(list);
// debugger;
// return list;
// });
// 这样是异步写法,打印的list的值还为空
setList(res.data.data.films);
// console.log(list);
});
}, []);
function handleChangePage(id) {
//query传参
// navigate(`/detail/?id=${id}`);
//路由传参
navigate(`/detail/${id}`)
}
return (
<div>
<ul>
{list.map((item) => (
<li
key={item.filmId}
onClick={() => {
handleChangePage(item.filmId)
}}
>
{item.name}
</li>
))}
</ul>
</div>
);
}
Detail.js
import React from "react";
import { useSearchParams, useParams, useNavigate } from "react-router-dom";
export default function Detail() {
const navigate =useNavigate()
// console.log(window.location.href);
// query传参
// const [searchParams, setsearchParams] = useSearchParams();
//拿到上个页面跳转过来传过来的id
//console.log(searchParams.get("id"));
// 路由传参拿到上个页面跳转过来传来的id的方法
const params=useParams()
// 看看params里是什么
// console.log(params);
console.log(params.myid);
return (
<div>
Detail
{/* query传参 */}
{/* */}
{/* 路由传参的跳转到另一个路由地址就得用navigate了 */}
<button onClick={() => navigate("/detail/1000")}>猜你喜欢</button>
</div>
);
}
import React from 'react'
import { useNavigate } from 'react-router-dom';
import Redirect from '../components/Redirect'
export default function Login() {
const navigate=useNavigate()
return (
<div>
<input type="text"></input>
<button type="button" onClick={()=>{localStorage.setItem("token","ss");
navigate("/center");
}}>登录</button>
</div>
)
}
index.js
import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect"
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Detail from "../views/Detail"
import Login from "../views/Login"
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
return (
<div>
<Routes>
{/* 默认路由 */}
{/* }> */}
<Route path="/film" element={<Film />}>
{/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
{/* }/> */}
{/* 默认路由写法2 重定向路径必须写绝对路径*/}
{/* } /> */ }
{/* 默认路由写法3 */}
<Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
{/* 这样的写法是将这两个组件加载到film组件中来 */}
<Route path="nowplaying" element={<Nowplaying />} />
<Route path="comingsoon" element={<Comingsoon />} />
</Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/cinema/search" element={<Search />}></Route>
{/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
{/* : }
> */}
<Route
path="/center"
element={<AuthComponent>
<Center></Center>
</AuthComponent>}></Route>
<Route path="/login" element={<Login />}></Route>
{/* }> */}
{/* 路由传参 */}
<Route path="/detail/:myid" element={<Detail />}></Route>
{/* 匹配到/ 重定向到film页面 */}
<Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
{/* 都没匹配上 跳转到404页面 *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
<Route path="*" element={<NotFound />}></Route>
</Routes>
</div>
);
}
// function isAuth(){
// return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({children}){
const isLogin=localStorage.getItem("token")
return isLogin?children:<Redirect to="/login"/>
}
BrowserRouter更像平时我们访问的路径,但是需要后端同步配置一下,不然会返回404
HashRouter会让路径多一个#符号
被Route包裹的组件会被赋予this.props.history,match等方法,但是被它包裹的组件的子组件没有这个方法,用withRouter组件可以实现,但是V6中没有这个方法了,需要自己封装
函数组件中如果要跳转网页,其实也用不到,可以用navigate(任何组件都可用),但是类组件没有hooks,需要封装withRouter方法
函数组件:将每个li变成一个新的组件FilmItem
Nowplaying.js
import React, { useEffect, useState} from "react";
import axios from "axios";
import FilmItem from "./FilmItem";
export default function Nowplaying() {
const [list, setList] = useState([]);
useEffect(() => {
axios(
"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
{
headers: {
"X-Client-Info":
'{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
"X-Host": "mall.film-ticket.film.list",
},
}
).then((res) => {
// console.log(res.data.data.films)
//setState函数是异步的,这样写是同步写法,可以及时获取到List的值
// setList((list)=>{
// list = res.data.data.films;
// console.log(list);
// debugger;
// return list;
// });
// 这样是异步写法,打印的list的值还为空
setList(res.data.data.films);
// console.log(list);
});
}, []);
return (
<div>
<ul>
{list.map((item) => (
// 将item传给FilmItem组件
<FilmItem key={item.filmId} {...item}></FilmItem>
))}
</ul>
</div>
);
}
FilmItem.js
import React from "react";
import { useNavigate } from "react-router-dom";
export default function FilmItem(item) {
const navigate = useNavigate();
function handleChangePage(id) {
//query传参
// navigate(`/detail/?id=${id}`);
//路由传参
navigate(`/detail/${id}`);
}
return (
<div>
<li
onClick={() => {
handleChangePage(item.filmId);
}}
>
{item.name}
</li>
</div>
);
}
import React from 'react'
import { useNavigate,useParams,useLocation } from 'react-router-dom'
export default function withRouter(Component) {
// 高阶函数高阶组件 接收一个组件,return出去一个组件,这里return了一个函数组件function(props){}
// Nowplaying所有属性传到这了
return function (props){
const push = useNavigate();
const params = useParams();
const location = useLocation()
// history里传个对象{push:push,params:params}
return <Component {...props} history={{ push, params,location }} />;
}
}
//withRouter(FilmItem)
FilmItem.js
// import React from "react";
// import { useNavigate } from "react-router-dom";
// export default function FilmItem(item) {
// const navigate = useNavigate();
// function handleChangePage(id) {
// //query传参
// // navigate(`/detail/?id=${id}`);
// //路由传参
// navigate(`/detail/${id}`);
// }
// return (
//
//
// onClick={() => {
// handleChangePage(item.filmId);
// }}
// >
// {item.name}
//
//
// );
// }
// 类组件
import React, { Component } from "react";
import withRouter from "../../components/withRouter";
class FilmItem extends Component {
handleChangePage(id){
// console.log(id);
this.props.history.push(`/detail/${id}`);
// this.props.history.push跳转页面
// this.props.history.match获取参数
// this.props.history.location获取当前路由
console.log(this.props.history);
}
render() {
console.log(this.props);
return (
<div>
<li
onClick={() => {
this.handleChangePage(this.props.filmId);
}}
>
{this.props.name}
</li>
</div>
);
}
}
export default withRouter(FilmItem)
由于路由过多,导致首页加载慢,渲染慢,我们使用路由懒加载后是按需加载且只加载一次
可以看到再次进入cinema不再加载,只加载了第一次
写成路由懒加载的写法后:
router文件夹下的index.js
import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect";
// import Film from "../views/Film";
// import Cinema from "../views/Cinema";
// import Search from "../views/Search";
// import Center from "../views/Center";
// import NotFound from "../views/NotFound";
// import Detail from "../views/Detail";
// import Login from "../views/Login";
// import Nowplaying from "../views/films/Nowplaying";
// import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
return (
<div>
<Routes>
{/* 默认路由 */}
{/* }> */}
<Route path="/film" element={LazyLoad("Film")}>
{/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
{/* }/> */}
{/* 默认路由写法2 重定向路径必须写绝对路径*/}
{/* } /> */ }
{/* 默认路由写法3 */}
<Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
{/* 这样的写法是将这两个组件加载到film组件中来 */}
{/* 路由懒加载写法:Nowplaying是films文件夹下的 */}
{/* } /> */}
<Route path="nowplaying" element={LazyLoad("films/Nowplaying")} />
<Route path="comingsoon" element={LazyLoad("films/Comingsoon")} />
</Route>
{/* 传进去的值是字符串,函数需要字符串,而不是变量,且找不到这个变量,如果有a=1,这里传变量a,函数就会找到1 */}
<Route path="/cinema" element={LazyLoad("Cinema")}></Route>
<Route path="/cinema/search" element={LazyLoad("Search")}></Route>
{/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
{/* : }
> */}
<Route
path="/center"
element={<AuthComponent>{LazyLoad("Center")}</AuthComponent>}
></Route>
<Route path="/login" element={LazyLoad("Login")}></Route>
{/* }> */}
{/* 路由传参 */}
<Route path="/detail/:myid" element={LazyLoad("Detail")}></Route>
{/* 匹配到/ 重定向到film页面 */}
<Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
{/* 都没匹配上 跳转到404页面 *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
<Route path="*" element={LazyLoad("NotFound")}></Route>
</Routes>
</div>
);
}
// function isAuth(){
// return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({ children }) {
const isLogin = localStorage.getItem("token");
return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`../views/${path}`));
return (
<React.Suspense fallback={<>加载中...</>}>
<Comp />
</React.Suspense>
);
};
import React from "react";
import { useRoutes, Navigate } from "react-router-dom";
import Redirect from "../components/Redirect";
export default function Mrouter() {
const element = useRoutes([
{
path: "/film",
element: LazyLoad("Film"),
// 子路径
children: [
// 重定向
{
path: "",
element: <Redirect to="/film/nowplaying"></Redirect>,
},
{
path: "nowplaying",
// 文件夹films下的Nowplaying 路径
element: LazyLoad("films/Nowplaying"),
},
{
path: "comingsoon",
element: LazyLoad("films/Comingsoon"),
},
],
},
{
path: "/cinema",
element: LazyLoad("Cinema"),
},
{
path: "/cinema/search",
element: LazyLoad("Search"),
},
{
path: "/login",
element: LazyLoad("Login"),
},
{
path: "/center",
element: <AuthComponent>{LazyLoad("Center")}</AuthComponent>,
},
{
path: "/detail/:myid",
element: LazyLoad("Detail"),
},
{
path: "/",
element: <Navigate to="/film"></Navigate>,
},
{
path: "*",
element: LazyLoad("NotFound"),
},
]);
return element;
}
function AuthComponent({ children }) {
const isLogin = localStorage.getItem("token");
return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`../views/${path}`));
return (
<React.Suspense fallback={<>加载中...</>}>
<Comp />
</React.Suspense>
);
};