React实例之完善布局菜单(一)

今天我们来用所学的知识来做一个布局菜单的组件, 针对这个组件我之前写过一个教程 React之布局菜单-CSDN博客,那个呢比较基础,这节课算是对那个教程的一个扩展和补充。这个实例讲完,这个系列就算告一段落了。先看效果在这里插入图片描述
这个教程要求对React知识的了解要求比较全面,如果你是跟着我这个系统文章一路学来的,应该就能跟得上学习进度。本教程内容很多,很详细,会分为几个章节来讲解。

安装

首先要安装MUI React RouterReact Redux这是必不可少的。我们除了会完成在开头的动图效果示例之外,还有较完整功能添加,所以,会用到一些没有讲过的功能。

设计前的考虑
  • 为了考虑到高度自定义这个特性,我们把菜单的配置以 json数组的形式进行配置。你可以把菜单配置放在服务器上,基于权限的考虑你甚至可以在后台根据用户权限的不同返回不同的菜单配置,实现角色的功能 。
  • 我们尽可能的将与业务代码无关的东西封装在组件内部,这样调用起来代码就很简洁。
  • 根据业务逻辑尽可能的细小化、模块化。
  • 所有UI元素都要适配暗黑模式,也就是两种颜色模式。
Bootstrap

前端是绕不开Css的,但是对于一个完整的项目来说,写Css就很繁琐,我的主张是,能偷懒就偷懒,不能偷懒想办法偷懒。这不,对于布局中的GridFlex 方面,Bootstrap 就提供了相当完美的功能了,我认为这方面它比MUI强许多,既然如此,何不做个拿来主义者呢,何苦自己为难自己呢。书回正传,回到我们的项目,在源目录(src) 下新建一个本章的实例目录:SMenu , 并在这个目录下新建目录 SCSS, 我们把网上下载的Bootstrap5.3的Css文件放到这个目录里。另外,我也提供了两个其它的两个css文件,目录结构如下所示:

在这里插入图片描述

关于Bootstrap的样式,请大家自行学习,此处不做详解。

以下是 components.css 的内容

.fade-enter {opacity: 0;transform: translateX(-100%);}.fade-enter-active {opacity: 1;transform: translateX(0%);}.fade-exit {opacity: 1;transform: translateX(0%);}.fade-exit-active {opacity: 0;transform: translateX(100%);}.fade-enter-active,.fade-exit-active {transition: opacity 500ms, transform 500ms;}.my-node-enter {opacity: 0;}.my-node-enter-active {opacity: 1;transition: opacity 200ms;}.my-node-exit {opacity: 1;}.my-node-exit-active {opacity: 0;transition: opacity 200ms;}/**弹窗动画*/.speedx-alert-enter {opacity: 0;transform: scale(0.9);}.speedx-alert-enter-active {opacity: 1;transform: translateX(0);transition: opacity 300ms, transform 300ms;}.speedx-alert-exit {opacity: 1;}.speedx-alert-exit-active {opacity: 0;transform: scale(0.9);transition: opacity 300ms, transform 300ms;}

下面是public.css的内容

html {background-color:#f2f2f2;
}body {margin: 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen','Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;font-size: 18px;line-height: 1.667;color: #222;text-align: justify;word-wrap: break-word;word-break: break-word;-moz-hyphens: auto;hyphens: auto;
}input,
textarea {font-family: 'Roboto', sans-serif;line-height: 1.4;background: #eee;
}code {font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;color: #353535d9;overflow-wrap: break-word;
}:not(pre) > code {background-color: rgb(214, 214, 214);border-radius: 3px;padding: 1px 3px;
}img {max-width: 100%;max-height: 20em;
}.page-container {position: relative;display: flex;flex-direction: column;background-color:white;min-height: 100vh;
}.layout-content{margin: 10px, 0px;padding: 0px;min-height: 100%;flex: 1;text-align:justify;
}.content-wrap {padding-bottom: 2.5rem;    /* Footer height */
}.footer {position: absolute;bottom: 0;width: 100%;height: 2.5rem;            /* Footer height */padding: 20px 0;/* box-shadow: 3px 0 5px #c9c9c9; */
}blockquote {border-left: 2px solid rgb(1, 154, 192);margin-left: 0;margin-right: 0;padding-left: 10px;color: rgb(150, 150, 150);font-style: italic;
}blockquote[dir='rtl'] {border-left: none;padding-left: 0;padding-right: 10px;border-right: 2px solid #ddd;
}input {box-sizing: border-box;font-size: 0.85em;width: 100%;padding: 0.5em;border: 2px solid #ddd;background: #fafafa;
}input:focus {outline: 0;border-color: blue;
}iframe {width: 100%;border: 1px solid #eee;
}[data-slate-editor] > * + * {margin-top: 1em;
}#root{display: flex;min-height: 100vh;flex-direction: column;background-color:#f2f2f2;
} .alignCenterVH{position: relative;top: 50%;transform: translateY(-50%);text-align: center;
}.mainBoxPosition{flex: 1;display: flex;justify-content: center;align-items: top;
}.titleInput {display: block;width: 100%;font-weight: bold;min-height: 50px;font-size: 22px;border:none;border-bottom: 1px;border-color: rgb(190, 190, 190);outline:none;
}.selectElement {display: block;max-width: 100%;max-height: 20em;
}.imgsubstring {display: block;color:rgb(116, 116, 116);font-weight: 500;font-size: medium;padding: 5px;text-align: center;
}.mayi-select {width: 400px; height: 200px;line-height: 200px;text-align: center;margin:auto;border: 1px solid #ccc;background: linear-gradient(#efefef,#ccc) padding-box,linear-gradient(135deg, rgba(0, 0, 0, 1) 25%, transparent 25%, transparent 50%, rgba(1, 1, 1, 1) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);background-size:100% 100%, 8px 8px;animation: bg 1s linear infinite;
}.mayi-select:hover{cursor: pointer;border: 1px dashed transparent;
}@keyframes bg {0% {background-position: 0 0;}100% {background-position: 8px 0;}
} .alignCenter {display: table-cell;/*垂直居中 */vertical-align: middle;/*水平居中*/text-align: center;/* text-align: center;background-color: #fff;position: absolute;left: 50%;top: 50%;transform: translate(-50%,-50%); */
}.site-layout-background{background-color: white;
}.check-background {width: 100px;height: 100px;background-image: url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg"\width="100" height="100" fill-opacity=".25">\<rect x="50" width="50" height="50"></rect>\<rect y="50" width="50" height="50"></rect>\</svg>');background-size:50px 50px;
}

设计一个样式组件,在App中引入一下就可以了,就可以保证我们的所有组件就能够应用到我们的样式。在STheme文件夹中创建 AdapterCss.jsx

import CssBaseline from '@mui/material/CssBaseline';
import '../SCSS/public.css';
import '../SCSS/components.css';
import '../SCSS/bootstrap5.3.0/bootstrap-utilities.min.css';
import '../SCSS/bootstrap5.3.0/bootstrap-grid.min.css';
// import '../SCSS/bootstrap5.3.0/bootstrap.min.css';export default function AdapterCss() {return <CssBaseline />
}

我们只要在根组件中引入一次这个样式适配器就好了。

颜色模式

因为我们要为App适配暗模式,所以在设计之初就要考虑好这个问题。首先,MUI所有的组件就已经适配了两种颜色模式,Bootstrap也是一样。还有一个就是我们自己封装的组件也要适配到暗模式中,就这要求我们自己设计的组件元素应用的颜色模式要么来处MUI, 要么采用Bootstrap,要么我们自己提供一个双模式的颜色体系。也就是说这三种不同框架之间的颜色体系是共存的。下面我们分别来说一说:

MUI的Theme模式

MUI 中提供了两个工具,让我们能构获取和设置颜色模式。

  • ThemeProvider 这个很好理解,就是一个颜色模式厂,就是一个Context;
  • createTheme 创建一个颜色模式。我们这里只是用它来改变MUI的颜色模式;

下面我们用示例说明用法:

import { createTheme } from '@mui/material/styles';function createMuiTheme(mode) {const themeMode = mode === "light" ? "light" : "dark";return createTheme({palette: {mode: themeMode,},});
}

上面的函数根据我们传入的模式关键字来创建相应的MUI颜色模式。

Bootstrap的颜色模式

这就简单了,我们只要改变顶层包裹组件的data-bs-theme属性值就可以切换颜色模式。

<div data-bs-theme="light"> 这是 light 模式 <div><div data-bs-theme="dark"> 这是 dark 模式 <div>

很简单吧。

自定义颜色模式

自定义颜色模式就有点技术含量了。也是最繁琐的一环。首先我们要定义的每种颜色要有两个模式下的颜色值。这就要一个标准,由于我没有采用 TS 设计模式,所以就要用其它的办法来约束定义的行为,比如一个函数就是一个很好的办法。

我们在STheme目录中创建一个工具函数库,把所有的我们自定义的工具函数放到其中统一导出就好了。

// SThemeUtils.jsximport SThemeCodors from "./SColors";// 生成基本颜色,lightColor为浅色,darkColor为深色
export function sColor(lightColor, darkColor) {return {light: lightColor,dark: darkColor,}
}/*** 生成主题模型* @param {} mode * @returns */
export default function createSTheme(mode = "light") {const themeMode = mode === "light" ? "light" : "dark";const sTheme = {mode: themeMode};Object.keys(SThemeCodors).forEach(key => {sTheme[key] = SThemeCodors[key][themeMode];});return sTheme;
}/*** 生成MUI系统主题* @param {*} mode * @returns */
export function createMuiTheme(mode) {const themeMode = mode === "light" ? "light" : "dark";return createTheme({palette: {mode: themeMode,},});
}
  • 我们通过sColor函数生成一个颜色对象,这样行为就统一了。每个颜色对象中都有一个 light 色 和一个 dark 色。所以我们设计之初就要把每种不同模式下的颜色配置好。这关系到我们整体的App风格。你看,我们设计一个App其实没那么简单对不对,对不同技术技能都要些要求的。
  • createSTheme根据自定义颜色模式生成基于自定义颜色的 theme`

现在就是定义颜色了,在相同的目录下,创建颜色库文件

// sColors.jsximport { sColor } from "./SThemeUtils";/*** 定义主题颜色模型*/
const SThemeColors = {bgColor: sColor("#edf3f2", "#1D1D1D"), //背景色/*** 菜单色配置*/badge: sColor("red", "red"), //小红点色menuBgcolor: sColor("#EEEEEE", "#0D2745"),//菜单栏的背景色hoverMenuBgcolor: sColor("#FFEACC", "#091C32"), //菜单栏背景色HovericonColorNormal: sColor("#1c2322", "#EEEEEE"), //图标色iconColorSquare: sColor("#363c3b", "#CCCCCC"), //无图标时的替代色menuNomalColor: sColor("#333333", "#07172A"), //菜单栏正常字体色activeMenuBgcolor: sColor("#FFEACC", "#1C54AD"), //活动菜单背景色activeBorderColor: sColor("#007AFF", "#1C54AD"), //活动菜单边框色menuSpliderColor: sColor("#DDDDDD", "#143C6A"), // 菜单栏分隔色menuSubitemColor: sColor("#545a59", "#B8B8B8"), //子菜单字体色hoverSubitemColor: sColor("#9fa2a1", "#3C628B"), //hover时的子菜单字体色hoverMenuSubitemBgcolor: sColor("#FFEACC", "#123862"), //子菜单的hover背景色activeMenuSubitemBgcolor: sColor("#FFBF66", "#0E2C4D"),//活动子菜单的背景色activeQuickMenuBgcolor: sColor("#FFBF66", "#2266B5"),//活动快捷菜单的子菜单
}export default SThemeColors;

这就是我们的颜色系统,根据需要自行定义。

创建 ThemeProvider

现在我们向App提供三种 provider, 还要提供 切换 模式的方法,最好的办法当然就是 Context了,我们来设计这几个Provider : 创建 SThemeContext.jsx文件:

// SThemeContext.jsximport { createContext } from 'react';/*** 创建自定义主题上下文*/
export const SThemeContext = createContext(null);export function CusThemeProvider({ theme, children }) {return (<SThemeContext.Provider value={theme}>{children}</SThemeContext.Provider>)
}/*** 创建切换主题上下文*/
export const ToggleSThemeContext = createContext(null);export function ToggleSThemeProvider({ handler, children }) {return (<ToggleSThemeContext.Provider value={handler}>{children}</ToggleSThemeContext.Provider>)
}/*** 创建Bootstrap主题上下文* @param {*} param0 * @returns */
export function BootstrapThemeProvider({ mode, children }) {return (<div data-bs-theme={mode}>{children}</div>)
}

文件里已经备注的很清楚了,就是创建两个上下文就OK了。

现在三种颜色的框架都有了。接下来我们就是要把这三个模式合并成一个Provider就完美了。我们来创建这个文件。在STheme目录下创建 SThemeProvider.jsx文件

// SThemeProvider.jsximport { useState } from 'react';
import { ThemeProvider } from '@mui/material/styles';
import AdapterCss from './AdapterCss';
import createSTheme, { createMuiTheme } from './SThemeUtils';
import { BootstrapThemeProvider, CusThemeProvider, ToggleSThemeProvider} from './SThemeContext';/*** 项目的皮肤供应器* @param {} param0 * @returns */
function SThemeProvider({ children }) {  const [theme, changeTheme] = useState({ custom: createSTheme("light"), muiTheme: createMuiTheme("light")});const toggleThemHandler = () => {const muiThemeMode = theme.muiTheme.palette.mode === "light" ? "dark" : "light";changeTheme({custom: createSTheme(muiThemeMode),muiTheme: createMuiTheme(muiThemeMode),})}return (<ThemeProvider theme={theme.muiTheme}><CusThemeProvider theme={theme.custom}><BootstrapThemeProvider mode={theme.custom.mode}><ToggleSThemeProvider handler={toggleThemHandler}><AdapterCss />{children}</ToggleSThemeProvider></BootstrapThemeProvider></CusThemeProvider></ThemeProvider>)
}export default SThemeProvider;

现在层次很清晰了吧。是不是清爽了许我,这样,我们在根组件中用 SThemeProvider包裹就好了。是不是很优雅。我们只需要在项目入口文件 main.jsx 中这样写就行了。

import React from 'react'
import ReactDOM from 'react-dom/client'
import SThemeProvider from './SMenu/STheme/SThemeProvider.jsx';
import App from './SMenu/App.jsx';ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><SThemeProvider><App /></SThemeProvider></React.StrictMode>,
)
编写主题切换Hook

这是主题最后一个环节,我们要提供一个 Hook 供我们的组件使用,要不然,设计主题有什么意义呢。

在 STheme目录中创建 文件 useToggleThemeHook.jsx

import { useContext } from 'react';
import { ToggleSThemeContext } from './SThemeContext';// 获取切换主题的功能函数。
const useToggleTheme = () => {return useContext(ToggleSThemeContext)
}export default useToggleTheme;

是不是太完美了。 是相当的完美啊。(未完待续)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/253992.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猫头虎分享已解决Bug ‍ || TypeError: Object of type ‘int64‘ is not JSON serializable

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

redis特点

一、redis线程模型有哪些&#xff0c;单线程为什么快&#xff1f; 1、IO模型维度的特征 IO模型使用了多路复用器&#xff0c;在linux系统中使用的是EPOLL 类似netty的BOSS,WORKER使用一个EventLoopGroup(threads1) 单线程的Reactor模型&#xff0c;每次循环取socket中的命令…

一篇文章理解时间复杂度和空间复杂度

今天也是很开心的学到了数据结构&#xff0c;也是打算把我自己对知识的理解给写出来了。第一篇数据结构开始咯。开始之前我们先理解一个概念。 什么是算法效率&#xff1f; 算法效率是指算法执行的速度或完成任务所需的资源&#xff08;如时间和空间&#xff09;的度量。它通…

字节3面真题,LeetCode上hard难度,极具启发性题解

文章目录 &#x1f680;前言&#x1f680;LeetCode&#xff1a;41. 缺失的第一个正整数&#x1f680;思路&#x1f680;整个代码思路串一下&#x1f680;Code &#x1f680;前言 铁子们好啊&#xff01;阿辉来讲道题&#xff0c;这道题据说是23年字节3面真题&#xff0c;LeetC…

jmeter的简单使用

1、打开jmeter 打开Jmeter 安装包&#xff0c;进入\bin 中&#xff0c;找到“ApacheJMeter.jar”或"jmeter.bat", 双击打开即可 2、建立线程组 如下图所示&#xff0c;右击TestPlan&#xff0c;点击ADD->Threads(Users)->ThreadGroup 线程组页面分析&#xf…

数字图像处理实验记录六(图像的傅里叶变换和频域处理)

前言&#xff1a; 一、基础知识 1&#xff0c;傅里叶变换是什么 傅里叶变换是一种线性积分变换&#xff0c;通俗来说&#xff0c;通过傅里叶变换就是把一段信号分解成若干个简谐波。 二、实验要求 1&#xff0e;产生一幅如图所示亮块图像f(x,y)&#xff08;256256 大小、…

【npm】修改npm全局安装包的位置路径

问题 全局安装的默认安装路径为&#xff1a;C:\Users\admin\AppData\Roaming\npm&#xff0c;缓存路径为&#xff1a;C:\Users\admin\AppData\Roaming\npm_cache&#xff08;其中admin为自己的用户名&#xff09;。 由于默认的安装路径在C盘&#xff0c;太浪费C盘内存啦&#…

网络协议与攻击模拟_15FTP协议

了解FTP协议 在Windows操作系统上使用serv-U软件搭建FTP服务 分析FTP流量 一、FTP协议 1、FTP概念 FTP&#xff08;文件传输协议&#xff09;由两部分组成&#xff1a;客户端/服务端&#xff08;C/S架构&#xff09; 应用场景&#xff1a;企业内部存放公司文件、开发网站时利…

[ChatGPT们】ChatGPT 如何辅助编程初探

主页&#xff1a;元存储的博客 全文 9000 字&#xff0c; 原创请勿转载。 我没有写过诗&#xff0c;但有人说我的代码像诗一样优雅 -- 雷军 图片来源&#xff1a;https://www.bilibili.com/video/BV1zL411X7oS/ 1. 引言 作为一个程序员&#xff0c;我们不仅要熟悉各种编程语…

C语言实现memcpy、memmove库函数

目录 引言一、库函数介绍二、库函数详解三、源码实现1.memcpy源码实现2.memmove源码实现 四、测试1.memcpy函数2.memmove函数 五、源码1.memcpy源码2.memmove源码 六、参考文献 引言 关于memcpy和memmove这两个函数&#xff0c;不论是算法竞赛还是找工作面试笔试&#xff0c;对…

百卓Smart管理平台 uploadfile.php 文件上传漏洞【CVE-2024-0939】

百卓Smart管理平台 uploadfile.php 文件上传漏洞【CVE-2024-0939】 一、 产品简介二、 漏洞概述三、 影响范围四、 复现环境五、 漏洞复现手动复现小龙验证Goby验证 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工…

vue3集成bpmn

文章目录 前言一、依赖二、汉化配置1.引入文件2.样式文件 总结 前言 vue3 集成bpmn 配置工作流 一、依赖 "bpmn-js": "^7.3.1", "bpmn-js-properties-panel": "^0.37.2", "bpmn-moddle": "^6.0.0", "camu…

【学网攻】 第(23)节 -- PPP协议

系列文章目录 目录 系列文章目录 文章目录 前言 一、PPP协议是什么&#xff1f; 二、实验 1.引入 实验目的 实验背景你是某公司的网络管理员&#xff0c;现在需要与另一个公司进行通信,需要你配置PPP协议保证双方发送的人是真正的而非黑客 技术原理 实验步骤新建Pack…

专业排版设计软件:QuarkXPress 2024 for mac中文激活版

QuarkXPress 2024 for Mac是一款功能强大、易于使用、高质量输出的专业排版软件。无论您是出版业的专家还是初学者&#xff0c;都可以通过QuarkXPress 2024轻松创建出令人惊叹的出版物。 软件下载&#xff1a;QuarkXPress 2024 for mac中文激活版下载 QuarkXPress 2023 for Mac…

牛客网SQL264:查询每个日期新用户的次日留存率

官网链接&#xff1a; 牛客每个人最近的登录日期(五)_牛客题霸_牛客网牛客每天有很多人登录&#xff0c;请你统计一下牛客每个日期新用户的次日留存率。 有一个登录(login。题目来自【牛客题霸】https://www.nowcoder.com/practice/ea0c56cd700344b590182aad03cc61b8?tpId82 …

前后端数据校验

前端校验内容 前端开发中的必要校验&#xff0c;可以保证用户输入的数据的准确性、合法性和安全性。同时&#xff0c;这些校验也有助于提供良好的用户体验和防止不必要的错误提交到后端。 1、必填字段校验&#xff1a; 对于必填的字段&#xff0c;需确保用户输入了有效的数据…

双非本科准备秋招(19.2)—— 设计模式之保护式暂停

一、wait & notify wait能让线程进入waiting状态&#xff0c;这时候就需要比较一下和sleep的区别了。 sleep vs wait 1) sleep 是 Thread 方法&#xff0c;而 wait 是 Object 的方法 2) sleep 不需要强制和 synchronized 配合使用&#xff0c;但 wait 强制和 s…

Javaweb之SpringBootWeb案例之登录校验功能的详细解析

2. 登录校验 2.1 问题分析 我们已经完成了基础登录功能的开发与测试&#xff0c;在我们登录成功后就可以进入到后台管理系统中进行数据的操作。 但是当我们在浏览器中新的页面上输入地址&#xff1a;http://localhost:9528/#/system/dept&#xff0c;发现没有登录仍然可以进…

【PowerShell】修改Windows网络配置的常用命令

PowerShell&#xff08;PS&#xff09;是一种强大的任务自动化和管理框架&#xff0c;具有丰富的命令和语法&#xff0c;可以用于编写脚本来管理Windows操作系统和其他应用程序。它的开放式架构和跨平台支持使得它成为一个灵活和可扩展的工具。 在网络配置方面&#xff0c;Powe…

Python3 交叉编译 numpy pandas scipy scikit-learn

1. 概述 由于需要将Python3.7 和一些软件包交叉编译到 armv7 平台硬件&#xff0c;如果是arm64位的系统&#xff0c;很多包都有预编译好的版本&#xff0c;可直接下载。本文主要在基于 crossenv(https://github.com/benfogle/crossenv)环境下交叉编译。 2. 编译环境搭建 创建…