赞
踩
编码式导航:基于JavaScript实现路由的跳转。
编码式导航:基于JavaScript实现路由的跳转。实际上是基于props中history对象中的相关方法实现路由跳转的。
在路由当中:
在react-router-dom的V5版本中:
如果是受控组件,则路由内容,会给每个组件传递三个属性:
history 提供了路由跳转的方式。
跳转路由
与新增历史记录
。跳转路由
与替换当前的历史记录
。历史记录中指定位置
。location:记录当前路由的信息。
match:记录当前路由匹配的规则与匹配记录。
如果是非受控组件,默认不会传递这三个属性。
基于路由内部提供的withRouter()高阶组件
,把非受控组件
变为受控组件
。
非受控组件
,也具备这三个属性信息,可适用于任何类型的组件
。import { withRouter } from "react-router-dom";
const CommonMenu = function CommonMenu(props) {
console.log(`CommonMenu-props`, props);
return <div></div>
}
export default withRouter(CommonMenu);
基于路由提供的useHistory()
/useLocation()
/useParams()
/useRouteMatch()
等Hook函数,在函数组件中获取对应的信息!
useHistory()
-> history对象
。useLocation()
-> location对象
。useRouteMatch()
-> match对象
。useParams()
-> 获取基于路径传参的信息。HashRouter()
/BrowserRouter()
等Router的内部!基于问号传参
传递信息。
URL地址栏
中可以看见(明文)。
丑
、不安全
、有长度限制
。urlencoded格式字符串
,接收的时候也需要处理这样的字符串。
qs库
或者new URLSearchParams()
处理。跳转后的目标组件
进行刷新操作
,因为传递的信息
在地址栏中有,所以依然可以获取问号传参信息
。基于隐式传参
传递信息。
传递的信息
在URL地址栏
中看不见。
不丑
、安全
、没有长度限制
。跳转后的目标组件
进行刷新操作
,因为地址栏
中没有传递的信息
,那么之前传递的信息
就消失了。基于路径参数
传递信息,把传参信息
作为地址的一部分
。
问号传参
一样,也是要把传递的信息
放在URL地址栏
中,所以相关特点和问号传参几乎一致。
问号传参
美观一些。path: '/home/message'
旧写法。path: '/home/message/:lx?/:name?'
新写法。
:
说明是传递的参数。?
可传可不传。match.params
/useParams()
处理即可!默认情况下,我们会把所有组件的代码全部打包到一个js文件中-即主js文件。这样当页面第一次渲染的时候,需要从服务器获取这个主js文件才能执行,但是因为其文件过大,需要加载很长时间,在此期间内,页面呈现白屏。
路由懒加载步骤:
在jsx文件及js代码中,使用src引入文件如图片这类需要使用文件地址时,要先用ES6把相对路径的文件导入进来再引用。或者使用绝对路径。
在css或less时,使用图片这类需要使用文件地址时,是可以直接使用相对路径的。
React阶段/day0603_router6/src/index.jsx
import React from "react";
import ReactDOM from "react-dom/client";
/* ANTD */
import { ConfigProvider } from "antd";
import zhCN from "antd/locale/zh_CN";
/* 组件&样式 */
import "./index.less";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
);
React阶段/day0603_router6/src/App.jsx 主视图文件,构建一级路由。
import React from "react"; // 1. 引入HashRouter, Routes, Route, Navigate等react-router-dom提供的组件。引入HashRouter用于把组件包起来,Routes用于设置路由信息组-把多条路由信息组合成一组以便只渲染一个路由视图,Route用于设置单条路由信息,Navigate用于与Route配合起来设置重定向。 import { HashRouter, Routes, Route, Navigate } from "react-router-dom"; // 2. 引入路由需要用到的业务视图组件。 import BasicLayout from "./layout/BasicLayout"; import UserLayout from "./layout/UserLayout"; import Error from "./layout/Error"; import Login from "./views/Login"; import Register from "./views/Register"; import Home from "./views/Home"; import Personal from "./views/Personal"; import Category from "./views/Category"; import Watch from "./views/home/Watch"; import Worker from "./views/home/Worker"; import Message from "./views/home/Message"; const App = function App() { // 3. 构建路由。 return ( <HashRouter> <Routes> <Route path="/" element={<BasicLayout />}> <Route path="" element={<Navigate to="/home" />} /> <Route path="home" element={<Home />}> <Route path="" element={<Navigate to="/home/watch" />} /> <Route path="watch" element={<Watch />} /> <Route path="worker" element={<Worker />} /> <Route path="message" element={<Message />} /> </Route> <Route path="category" element={<Category />} /> <Route path="personal" element={<Personal />} /> </Route> <Route path="/user" element={<UserLayout />}> <Route path="" element={<Navigate to="/user/login" />} /> <Route path="login" element={<Login />} /> <Route path="register" element={<Register />} /> </Route> <Route path="/404" element={<Error />} /> <Route path="*" element={<Navigate to="/404" />} /> </Routes> </HashRouter> ); }; export default App;
React阶段/day0603_router6/src/layout
React阶段/day0603_router6/src/layout/BasicLayout.jsx 一级路由组件。
import { useState } from "react" import { Menu, Button } from "antd" import { HomeOutlined, ClusterOutlined, UserOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons' import styled from "styled-components" import logo from "@/assets/images/logo.svg" import avatar from "@/assets/images/touxiang.png" import { Outlet } from "react-router-dom" /* 组件样式 */ const BasicLayoutStyle = styled.div` height: 100%; overflow: hidden; .header{ box-sizing: border-box; padding: 0 15px; height: 48px; background: #001529; display: flex; justify-content: space-between; align-items: center; .logo, .info{ line-height: 48px; font-size: 18px; color: #FFF; display: flex; align-items: center; img{ margin-right: 10px; width: 35px; height: 35px; } } .info{ font-size: 14px; img{ width: 30px; height: 30px; } } } .content{ height: calc(100% - 48px); display: flex; .menu-box{ height: 100%; background: #001529; .ant-btn{ margin-left: 4px; background: transparent; box-shadow: none; } .ant-menu-item{ padding: 0 24px; } .ant-menu-inline-collapsed{ .ant-menu-item{ padding: 0 28px; } } } .view-box{ box-sizing: border-box; padding: 15px; height: 100%; flex-grow: 1; .component{ height: 100%; overflow-y: auto; overflow-x: hidden; background: #FFF; } } } ` const BasicLayout = function BasicLayout() { // 左侧Menu的数据 const menuItem = [{ key: 'home', label: '控制面板', icon: <HomeOutlined /> }, { key: 'category', label: '分类管理', icon: <ClusterOutlined /> }, { key: 'personal', label: '个人中心', icon: <UserOutlined /> }] // 定义状态 let [collapsed, setCollapsed] = useState(false) return <BasicLayoutStyle> <div className="header"> <h2 className="logo"> <img src={logo} alt="" /> Ant Design </h2> <div className="avatar"> <p className="info"> <img src={avatar} alt="" /> 海贼王-路飞 </p> </div> </div> <div className="content"> <div className="menu-box"> <Button type="primary" onClick={() => { setCollapsed(!collapsed) }}> {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />} </Button> <Menu mode="inline" theme="dark" items={menuItem} defaultSelectedKeys={['home']} inlineCollapsed={collapsed} /> </div> <div className="view-box"> <div className="component"> {/* 主页的二级路由 */} <Outlet /> </div> </div> </div> </BasicLayoutStyle> } export default BasicLayout
React阶段/day0603_router6/src/layout/Error.jsx 一级路由组件。
import React from "react"; import { Button, Empty } from "antd"; import styled from "styled-components"; /* 组件的样式 */ const ErrorStyle = styled.div` height: 100%; overflow: hidden; .ant-empty { position: relative; top: 100px; } `; const Error = function Error() { return ( <ErrorStyle> <Empty description={<span>很遗憾,您访问的页面不存在!</span>}> <Button type="primary">返回首页</Button> </Empty> </ErrorStyle> ); }; export default Error;
React阶段/day0603_router6/src/layout/UserLayout.jsx 一级路由组件。
import React from "react"; import { Tabs } from "antd"; import styled from "styled-components"; import backgroundImg from "@/assets/images/background.svg"; import logo from "@/assets/images/logo.svg"; import { Outlet } from "react-router-dom"; /* 组件样式 */ const UserLayoutStyle = styled.div` position: relative; height: 100%; background: url(${backgroundImg}) no-repeat; background-size: 100%; .content { position: absolute; top: 100px; left: 50%; margin-left: -165px; width: 330px; } .header { .title { display: flex; justify-content: center; align-items: center; line-height: 44px; font-size: 33px; font-weight: normal; img { margin-right: 10px; width: 44px; height: 44px; } } .subtitle { margin: 12px 0 40px 0; text-align: center; font-size: 14px; color: rgba(0, 0, 0, 0.45); } } .ant-tabs { margin-bottom: 10px; .ant-tabs-nav { &:before { border-bottom-color: #ddd; } } .ant-tabs-tab { padding: 0 10px; font-size: 16px; line-height: 40px; } } `; const UserLayout = function UserLayout() { // Tab页卡的数据 const tabItem = [ { label: `用户登录`, key: "login", }, { label: `用户注册`, key: "register", }, ]; return ( <UserLayoutStyle> <div className="content"> <div className="header"> <h1 className="title"> <img src={logo} alt="" /> <span>Ant Design</span> </h1> <p className="subtitle"> Ant Design 是西湖区最具影响力的 Web 设计规范 </p> </div> <Tabs centered defaultActiveKey="login" items={tabItem} /> {/* 登录/注册二级路由的位置 */} <Outlet /> </div> </UserLayoutStyle> ); }; export default UserLayout;
React阶段/day0603_router6/src/views 页面组件,一般是二级及以下的业务组件。
React阶段/day0603_router6/src/views/home 二级路由组件-Home.jsx下的三级路由组件。
React阶段/day0603_router6/src/views/home/Message.jsx
import React from "react";
const Message = function Message() {
return <div>控制面板 - 消息中心</div>;
};
export default Message;
React阶段/day0603_router6/src/views/home/Watch.jsx
import React from "react";
const Watch = function Watch() {
return <div>控制面板 - 数据监控</div>;
};
export default Watch;
React阶段/day0603_router6/src/views/home/Worker.jsx
import React from "react";
const Worker = function Worker() {
return <div>控制面板 - 工作台</div>;
};
export default Worker;
React阶段/day0603_router6/src/views/Category.jsx
import React from "react";
const Category = function Category() {
return <div>分类管理的内容</div>;
};
export default Category;
React阶段/day0603_router6/src/views/Home.jsx 二级路由组件。
import React from "react"; import styled from "styled-components"; import { NavLink, Outlet } from "react-router-dom"; /* 组件样式 */ const HomeStyle = styled.div` padding: 10px; .nav-box { display: flex; border-bottom: 1px solid #eee; a { padding: 0 20px; line-height: 40px; font-size: 15px; color: #000; &.active { color: #1677ff; font-size: 16px; } } } `; const Home = function Home() { return ( <HomeStyle> <nav className="nav-box"> <NavLink to="/home/watch">数据监控</NavLink> <NavLink to="/home/worker">工作台</NavLink> <NavLink to="/home/message">消息中心</NavLink> </nav> {/* 首页的三级路由 */} <Outlet /> </HomeStyle> ); }; export default Home;
React阶段/day0603_router6/src/views/Login.jsx 二级路由组件。
import React from "react"; import { Form, Input, Checkbox, Button } from "antd"; import { UserOutlined, LockOutlined } from "@ant-design/icons"; import styled from "styled-components"; /* 组件的样式 */ const LoginStyle = styled.div` .remember { display: flex; justify-content: space-between; align-items: center; a { font-size: 14px; color: #1890ff; } } .submit { width: 100%; } `; const Login = function Login() { return ( <LoginStyle> <Form autoComplete="off" initialValues={{ account: "", password: "", }} > <Form.Item name="account"> <Input size="large" placeholder="请输入账号" prefix={<UserOutlined />} /> </Form.Item> <Form.Item name="password"> <Input.Password size="large" placeholder="请输入密码" prefix={<LockOutlined />} /> </Form.Item> <Form.Item> <div className="remember"> <Checkbox checked>记住账号密码</Checkbox> <a href="">忘记密码?</a> </div> </Form.Item> <Form.Item> <Button className="submit" type="primary" size="large"> 立即登录 </Button> </Form.Item> </Form> </LoginStyle> ); }; export default Login;
React阶段/day0603_router6/src/views/Personal.jsx
import React from "react";
const Personal = function Personal() {
return <div>个人中心的内容</div>;
};
export default Personal;
React阶段/day0603_router6/src/views/Register.jsx
import React from "react"; import { Form, Input, Button } from "antd"; import { LockOutlined, PhoneOutlined } from "@ant-design/icons"; import styled from "styled-components"; /* 组件的样式 */ const RegisterStyle = styled.div` .submit { width: 100%; } .code-box { display: flex; justify-content: space-between; align-items: center; .ant-form-item { &:nth-child(1) { margin-right: 10px; } } } `; const Register = function Register() { return ( <RegisterStyle> <Form autoComplete="off" initialValues={{ phone: "", code: "", }} > <Form.Item name="phone"> <Input size="large" placeholder="请输入手机号" prefix={<PhoneOutlined />} /> </Form.Item> <div className="code-box"> <Form.Item name="code"> <Input size="large" placeholder="请输入验证码" prefix={<LockOutlined />} /> </Form.Item> <Form.Item> <Button size="large">发送验证码</Button> </Form.Item> </div> <Form.Item> <Button className="submit" type="primary" size="large"> 立即注册 </Button> </Form.Item> </Form> </RegisterStyle> ); }; export default Register;
在主视图文件里引入构建一级路由。
<HashRouter></HashRouter>
所有需要用到路由信息的,都要用这个组件包起来。
<Routes></Routes>
包裹一组路由信息。
<Route>标签
,放其它标签会报错。路由匹配规则
或路由表
都写在一起。<Route/>
与<Route></Route>
用来设置一条路由信息。
<Route></Route>
内部可以嵌套<Route/>
与<Route></Route>
。
默认匹配规则类似于
精准匹配。
被嵌套的<Route/>
标签就是子级路由。
子级路由可以省略父级路由地址。
/
开头,就会被认为是绝对路径。/
开头,那么其前方就会自动添加上父级路由的地址。要渲染的组件用element属性来设置,但element属性的值必须是一个组件标签的格式,而不能只是一个函数变量。
<Route path="personal" element={<Personal />} />//一个组件标签的格式,是对的。
<Route path="personal" element={Personal} />//一个函数变量,是错的。
import React from "react"; // 1. 引入HashRouter, Routes, Route, Navigate等react-router-dom提供的组件。引入HashRouter用于把组件包起来,Routes用于设置路由信息组-把多条路由信息组合成一组以便只渲染一个路由视图,Route用于设置单条路由信息,Navigate用于与Route配合起来设置重定向。 import { HashRouter, Routes, Route, Navigate } from "react-router-dom"; // 2. 引入路由需要用到的业务视图组件。 import BasicLayout from "./layout/BasicLayout"; import UserLayout from "./layout/UserLayout"; import Error from "./layout/Error"; import Login from "./views/Login"; import Register from "./views/Register"; import Home from "./views/Home"; import Personal from "./views/Personal"; import Category from "./views/Category"; import Watch from "./views/home/Watch"; import Worker from "./views/home/Worker"; import Message from "./views/home/Message"; const App = function App() { // 3. 构建路由。 return ( <HashRouter> <Routes> <Route path="/" element={<BasicLayout />}> <Route path="" element={<Navigate to="/home" />} /> <Route path="home" element={<Home />}> <Route path="" element={<Navigate to="/home/watch" />} /> <Route path="watch" element={<Watch />} /> <Route path="worker" element={<Worker />} /> <Route path="message" element={<Message />} /> </Route> <Route path="category" element={<Category />} /> <Route path="personal" element={<Personal />} /> </Route> <Route path="/user" element={<UserLayout />}> <Route path="" element={<Navigate to="/user/login" />} /> <Route path="login" element={<Login />} /> <Route path="register" element={<Register />} /> </Route> <Route path="/404" element={<Error />} /> <Route path="*" element={<Navigate to="/404" />} /> </Routes> </HashRouter> ); }; export default App;
<Route path="*" element={<Navigate to="/404" />} />
用于设置路由跳转。
在父级路由匹配组件的指定位置,基于<Outlet/>组件
把子级路由匹配的内容进行渲染!
// 1. 引入Outlet组件。
import { Outlet } from "react-router-dom"
{/* 2. 在父级路由匹配组件的指定位置,基于`<Outlet/>组件`把子级路由匹配的内容进行渲染! */}
<Outlet />
import { useState } from "react" import { Menu, Button } from "antd" import { HomeOutlined, ClusterOutlined, UserOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons' import styled from "styled-components" import logo from "@/assets/images/logo.svg" import avatar from "@/assets/images/touxiang.png" // 1. 引入Outlet组件。 import { Outlet } from "react-router-dom" /* 组件样式 */ const BasicLayoutStyle = styled.div` height: 100%; overflow: hidden; .header{ box-sizing: border-box; padding: 0 15px; height: 48px; background: #001529; display: flex; justify-content: space-between; align-items: center; .logo, .info{ line-height: 48px; font-size: 18px; color: #FFF; display: flex; align-items: center; img{ margin-right: 10px; width: 35px; height: 35px; } } .info{ font-size: 14px; img{ width: 30px; height: 30px; } } } .content{ height: calc(100% - 48px); display: flex; .menu-box{ height: 100%; background: #001529; .ant-btn{ margin-left: 4px; background: transparent; box-shadow: none; } .ant-menu-item{ padding: 0 24px; } .ant-menu-inline-collapsed{ .ant-menu-item{ padding: 0 28px; } } } .view-box{ box-sizing: border-box; padding: 15px; height: 100%; flex-grow: 1; .component{ height: 100%; overflow-y: auto; overflow-x: hidden; background: #FFF; } } } ` const BasicLayout = function BasicLayout() { // 左侧Menu的数据 const menuItem = [{ key: 'home', label: '控制面板', icon: <HomeOutlined /> }, { key: 'category', label: '分类管理', icon: <ClusterOutlined /> }, { key: 'personal', label: '个人中心', icon: <UserOutlined /> }] // 定义状态 let [collapsed, setCollapsed] = useState(false) return <BasicLayoutStyle> <div className="header"> <h2 className="logo"> <img src={logo} alt="" /> Ant Design </h2> <div className="avatar"> <p className="info"> <img src={avatar} alt="" /> 海贼王-路飞 </p> </div> </div> <div className="content"> <div className="menu-box"> <Button type="primary" onClick={() => { setCollapsed(!collapsed) }}> {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />} </Button> <Menu mode="inline" theme="dark" items={menuItem} defaultSelectedKeys={['home']} inlineCollapsed={collapsed} /> </div> <div className="view-box"> <div className="component"> {/* 主页的二级路由 */} {/* 2. 在父级路由匹配组件的指定位置,基于`<Outlet/>组件`把子级路由匹配的内容进行渲染! */} <Outlet /> </div> </div> </div> </BasicLayoutStyle> } export default BasicLayout
react-router-dom(V6)
与react-router-dom(V5)
对比都是基于HashRouter()
与BrowserRouter()
来指定路由的模式,而且建议所有的组件都放在HashRouter()与BrowserRouter()这类Router
中。
V6版本中没有了<Switch>
,因为不需要了,其内部自己做了类似于<Switch>
的处理。
V6中没有了<Redirect>
,但是可以基于<Navigate to="/404"/>
来代替它。
<Redirect />
相当于特殊的<Route/>
,我们是基于它设置重定向的规则的。
<Redirect from="*" to="/404" />
<Navigate/>
是一个组件,渲染这个组件时会重定向到该组件的to属性指定的地址
。
<Route path="*" element={<Navigate to="/404" />} />
V6中也是基于<Route/>
设置匹配规则,只不过都需要放在<Routes></Routes
>中,而且在<Route/>
上设置的详细规则,和V5版本有很大的区别:
<Route/>
上设置exact属性
用于进行精准匹配
了,因为默认都是类似于精准匹配
。<Route/>
上path属性
和之前一样,设置对应的匹配地址。渲染那个组件
不再基于<Route/>
上component属性
处理,而是基于<Route/>
上element属性
渲染,而且也没有了<Route/>
上render函数属性
这样的配置,element
渲染的内容需要写成<Component/>
这种格式!
<Routes></Routes>
只能放<Route/>
。
V5旧:
import { HashRouter, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom' import BasicLayout from "./layout/BasicLayout" import UserLayout from "./layout/UserLayout" import Error from "./layout/Error" <HashRouter> <Switch> <Route path="/user" component={UserLayout} /> <Route path="/404" component={Error} /> <Route path="/" component={BasicLayout} /> <Redirect to="/404" /> </Switch> </HashRouter> //或 <HashRouter> <Route path="/user" component={UserLayout} /> </HashRouter>
V6新:
import { HashRouter, Routes, Route, Navigate } from "react-router-dom"; import BasicLayout from "./layout/BasicLayout"; import UserLayout from "./layout/UserLayout"; import Error from "./layout/Error"; <HashRouter> <Routes> <Route path="/" element={<BasicLayout />}></Route> <Route path="/user" element={<UserLayout />}></Route> <Route path="/404" element={<Error />} /> <Route path="*" element={<Navigate to="/404" />} /> </Routes> </HashRouter> //或 <HashRouter> <Routes> <Route path="/" element={<BasicLayout />}></Route> </Routes> </HashRouter>
V6有一个重大的变革:所有级别的路由匹配规则
或路由表
都写在一起,而不再像V5一样,需要分散到指定组件的特定位置
上,这样有利于路由的统一管理
!
<Route path="/user" element={<UserLayout />}>
/* - 在`子级路由中`其path可以省略`父级路由的地址`
- 但是也不要自己再加`/`
- `path=""` 等价于 `path="/user"`
- `path="login"`等价于 `path="/user/login"`
- 如果路由地址是`/user/xxx`,首先和一级路由`/user`进行匹配了,渲染`UserLayout组件`后,进入`/user`的`子级路由`进行匹配;
- 在`子级路由`中`一项项地进行匹配`,匹配成功,就把`子级路由`中`element属性指定的组件`,放在`UserLayout组件`中的`<Outlet>容器`中进行渲染!
- 如果在`子级路由`中没有任何匹配的,则跳出这一级,继续向下匹配`与/user路由同级`的`其它的一级路由`! */
<Route path="" element={<Navigate to="/user/login" />} />
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
</Route>
在子级路由中
其path可以省略父级路由的地址
。
/
。path=""
等价于 path="/user"
。path="login"
等价于 path="/user/login"
。如果路由地址是/user/xxx
,首先和一级路由/user
进行匹配了,渲染UserLayout组件
后,进入/user
的子级路由
进行匹配;
子级路由
中一项项地进行匹配
,匹配成功,就把子级路由
中element属性指定的组件
,放在UserLayout组件
中的<Outlet>容器
中进行渲染!子级路由
中没有任何匹配的,则跳出这一级,继续向下匹配与/user路由同级
的其它的一级路由
!但是需要在上级路由匹配组件的指定位置,基于<Outlet/>
把下级路由匹配的内容进行渲染!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。