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.hqwc.cn/news/451154.html

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

相关文章

Java中动态修改注解的值

1. 描述 部分场景需要动态修改注解的值。例如&#xff0c;我们使用自定义注解控制接口流量&#xff0c;如果需要动态修改流量值&#xff0c;可以使用反射的方法实现。 2. 步骤 获取注解从注解中获取memberValues属性(map)使用put方法更新对象的值 3. 代码实现 该部分代码主…

YOLO部署实战(3):Darknet训练模型权重

1 一些概念和问题 YOLO中的darknet到底指的是什么&#xff1f; darknet到底是一个类似于TensorFlow、PyTorch的框架&#xff0c;还是一个类似于AlexNet、VGG的模型&#xff1f; 其实都是。YOLO作者自己写的一个深度学习框架叫darknet&#xff08;见YOLO原文2.2部分&#xff…

Hexo设置少量固定的动态背景图

文章目录 前言先准备素材问题分析代码实现逻辑写在哪先搭建基本框架然后添加图片链接动画效果 前言先准备素材问题分析代码实现逻辑写在哪先搭建基本框架然后添加图片链接动画效果 前言 在以前的这篇文章中&#xff0c;我们设置了一些动态背景。 这次我们加一丁点优化&#x…

MySQL 用户管理

重点&#xff1a; 视图&#xff0c;函数&#xff0c;存储过程&#xff0c;触发器&#xff0c;事件&#xff08; 了解 &#xff09; 用户管理&#xff0c;密码管理 grant revoke 权限管理 MySQL 架构&#xff08; 了解 &#xff09; 存储引擎&#xff1a;MyISAM 和 InnoDB …

展馆设计的必备要素有哪些

1、空间的设计 展馆要想配得上优秀这两个字眼&#xff0c;那么比较基本的表现就是要具有美感&#xff0c;要规划&#xff0c;合理美观的造型&#xff0c;并在此基础上重视互动融合&#xff0c;既要拥有特色具有创意的风格&#xff0c;又要能够和整个空间和谐又统一。布展内容结…

Python入门:生成器迭代器

一、列表生成式 现在有个需求&#xff0c;列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&#xff0c;要求你把列表里的每个值加1&#xff0c;怎么实现&#xff1f;你可能会想到2种方式 二逼青年版 1 2 3 4 a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b [] for i in a:b.append(i1) print(b) …

ele-h5项目使用vue3+vite+vant4开发:第四节、业务组件-SearchView组件开发

需求分析 展示切换动画搜索框输入文字&#xff0c;自动发送请求搜索结果展示搜索状态维护历史搜索展示&#xff0c;点击历史搜索后发送请求历史搜索更多切换动画效果 <script setup lang"ts"> import OpSearch from /components/OpSearch.vue import { ref } f…

Sequine - Sequencing Animation and Visual Scripting

Sequine是Unity的通用序列工具和可视化脚本。您还可以按需播放动画片段,而不受 Animator 控制器的限制! 以下是可以使用工具进行的工作: 直接序列动画剪辑,不受animator控制器的限制 用可堆叠行为序列文本动画 序列脚本功能/命令执行 序列可以存在于任何类型的Unity对象中,…

05 MP之ActiveRecord模式+SimpleQuery

1. ActiveRecord ActiveRecord(活动记录&#xff0c;简称AR)&#xff0c;是一种领域模型模式&#xff0c;特点是一个模型类对应关系型数据库中的一个表&#xff0c;而模型类的一个实例对应表中的一行记录。 其目标是通过围绕一个数据对象, 进行全部的CRUD操作。 1.1 让实体类…

nginx初学者指南

一、启动、停止和重新加载配置 前提&#xff1a;先要启动nginx 在Windows上启动nginx的步骤如下&#xff1a; 1. 下载并安装nginx。可以从nginx官网下载适合自己操作系统的版本&#xff0c;一般是zip压缩包&#xff0c;解压到指定目录中。 2. 进入nginx的安装目录&#xff…

【LeetCode】每日一题 2024_2_4 Nim 游戏(找规律,博弈论)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;Nim 游戏题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;Nim 游戏 题目链接&#xff1a;292. Nim 游戏 题目描述 代码与解题…

Leetcode—32. 最长有效括号【困难】(动态规划及ranges::max()使用)

2024每日刷题&#xff08;110&#xff09; Leetcode—32. 最长有效括号 栈实现代码 class Solution { public:int longestValidParentheses(string s) {stack<int> st;st.push(-1);int n s.size();int maxn 0;for(int i 0; i < n; i) {if(s[i] () {st.push(i);}…