Material UI 登录(Sign-in)模板实战:复用官方模板搭建带主题切换、表单校验与忘记密码流程的登录页
【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui
本篇指南基于 Material UI 官方仓库中的 Sign-in 模板文档与源码,讲解如何把仓库内置的登录页模板复制到自己的项目中使用,并深入拆解模板中SignIn主组件的表单校验、忘记密码弹窗、共享主题系统(AppTheme+themePrimitives)以及亮色/暗色模式切换的实现原理,帮助你在不手写布局与主题代码的前提下,快速得到一个可运行、可定制的 Material Design 登录页。
模板定位与使用方式
Sign-in 模板属于 Material UI 官方免费 React 模板集的一部分。根据 templates.md 的说明,该模板集包含 Dashboard、Marketing Page、Checkout、Sign-in/Sign-up 和 Blog 等页面,每个模板都提供两套主题(自定义主题与默认 Material Design 2 主题),且均支持亮色/暗色模式;页面各区块通过注释或独立文件划分,可以单独抽取 Hero、Footer 等片段复用到其他页面。
关于 Sign-in 模板的官方使用文档见 README.md,其给出的三步使用流程如下:
- 将
sign-in和shared-theme两个目录复制到你的项目中,或者复制进仓库自带的示例项目(见 examples 目录下的 Next.js、Vite、Preact、Remix 等示例工程); - 确保项目具备所需依赖:
@mui/material、@mui/icons-material、@emotion/styled、@emotion/react; - 导入并使用
SignIn组件。
其中第 2 步依赖值得注意:@emotion/styled与@emotion/react是styledAPI 和sx属性的运行基础——模板中的Card、SignInContainer两个样式化组件(见下文)都依赖@mui/material/styles的styled。
模板文件结构
Sign-in 模板的完整源码位于 sign-in 目录,所有关键文件都有JS 与 TSX 双版本(如SignIn.js/SignIn.tsx),可以按项目的语言栈选择使用。核心结构如下:
sign-in/ ├── SignIn.js / SignIn.tsx # 登录页主组件(入口) ├── components/ │ ├── CustomIcons.js / .tsx # Sitemark、Google、Facebook 内联 SVG 图标 │ └── ForgotPassword.js / .tsx # 忘记密码弹窗 └── ../shared-theme/ # 模板间共享的主题层 ├── AppTheme.js / .tsx # ThemeProvider 封装 ├── themePrimitives.ts / .js # 设计令牌:调色板、字体、形状、阴影 ├── ColorModeSelect.* # 亮/暗/跟随系统切换下拉框 └── customizations/ # 按组件类别拆分的组件级样式 ├── inputs.tsx ├── dataDisplay.tsx ├── feedback.tsx ├── navigation.tsx └── surfaces.ts从源码结构看,shared-theme被多个模板(blog、checkout、dashboard、sign-in、sign-up、crud-dashboard)共同引用,sign-in/SignIn.tsx第 17~18 行直接import AppTheme from '../shared-theme/AppTheme'与ColorModeSelect from '../shared-theme/ColorModeSelect',因此官方文档才要求两个目录一起复制——只复制sign-in而不带shared-theme,相对导入会直接失效。
SignIn 主组件解析
SignIn.tsx 是整个模板的入口组件,签名为SignIn(props: { disableCustomTheme?: boolean })。disableCustomTheme属性仅在 MUI 文档站预览时置为true以绕过文档站自身主题,在自己的项目里可以忽略。
布局与样式化容器
组件顶部定义了两个styled组件(SignIn.tsx#L21-L61):
Card:基于@mui/material/Card定制,纵向 flex 布局、alignSelf: 'center'水平居中、margin: 'auto'垂直居中;在sm断点以上限宽450px,padding与gap使用theme.spacing(4)/theme.spacing(2)保持与主题刻度一致;boxShadow使用双层柔和阴影,并通过theme.applyStyles('dark', {...})在暗色模式下换用更深、更重的阴影值;SignInContainer:基于Stack定制,height: 'calc((1 - var(--template-frame-height, 0)) * 100dvh)'配合minHeight: '100%'撑满视口高度,并用::before伪元素铺一层radial-gradient径向渐变背景(暗色模式下切换为深蓝灰渐变),营造登录页常见的“卡片悬浮在渐变背景上”的效果。
--template-frame-height这个 CSS 变量对应主题中cssVarPrefix: 'template'的配置(见下文),默认取0,即容器高度为整个视口。
状态与表单校验
组件用React.useState维护五段状态(SignIn.tsx#L63-L115):emailError/emailErrorMessage/passwordError/passwordErrorMessage以及控制忘记密码弹窗的open。校验逻辑集中在validateInputs:
const validateInputs = () => { const email = document.getElementById('email') as HTMLInputElement; const password = document.getElementById('password') as HTMLInputElement; let isValid = true; if (!email.value || !/\S+@\S+\.\S+/.test(email.value)) { setEmailError(true); setEmailErrorMessage('Please enter a valid email address.'); isValid = false; } else { setEmailError(false); setEmailErrorMessage(''); } if (!password.value || password.value.length < 6) { setPasswordError(true); setPasswordErrorMessage('Password must be at least 6 characters long.'); isValid = false; } else { setPasswordError(false); setPasswordErrorMessage(''); } return isValid; };两条校验规则:邮箱必须匹配正则/\S+@\S+\.\S+/;密码长度至少 6 个字符。提交时handleSubmit会再次检查emailError || passwordError,任一存在则event.preventDefault()拦截提交;校验通过后从FormData中取出email与password(模板里仅console.log,实际项目中应在此处接入你的认证后端)。
表单结构与社交登录
渲染部分(SignIn.tsx#L117-L231)组装了完整的登录卡片:
<AppTheme>包裹整页,内部第一行是<CssBaseline enableColorScheme />,负责全局样式重置并启用颜色方案(color scheme)切换能力;- 右上角固定一个
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />用于切换 System/Light/Dark; - 卡片内依次为
SitemarkIcon品牌图标、h4标题(字号用clamp(2rem, 10vw, 2.15rem)做流式缩放)、noValidate的<form>,内含 Email / Password 两个TextField(error、helperText绑定校验状态,autoComplete分别取email与current-password)、"Remember me" 复选框、ForgotPassword弹窗、提交按钮,以及<Divider>or</Divider>分隔线下方的 Google / Facebook 社交登录按钮(模板中为alert占位)和指向注册页的链接。
社交按钮的图标来自 CustomIcons.tsx:SitemarkIcon、FacebookIcon、GoogleIcon均为@mui/material/SvgIcon封装的内联 SVG,无需额外图标资源文件即可渲染,也可整体替换成你项目自己的 Logo。
忘记密码弹窗
ForgotPassword.tsx 接收open与handleClose两个属性,主体是一个Dialog,其写法有两个值得学习的点:
- 通过
slotProps.paper把 Dialog 的 Paper 直接变成<form>(ForgotPassword.tsx#L17-L30):
<Dialog open={open} onClose={handleClose} slotProps={{ paper: { component: 'form', onSubmit: (event: React.SubmitEvent<HTMLFormElement>) => { event.preventDefault(); handleClose(); }, sx: { backgroundImage: 'none' }, }, }} >这样弹窗内的 "Continue" 按钮可以直接用type="submit"触发表单提交,同时sx: { backgroundImage: 'none' }去掉了 Paper 从主题继承的渐变背景,保持弹窗视觉干净。
- 弹窗内含一个
autoFocus的OutlinedInput(邮箱输入)与DialogActions中的 Cancel / Continue 按钮,"发送邮件重置链接" 的后端调用留给你自行接入。
共享主题系统(shared-theme)
模板视觉一致性的核心在 shared-theme 目录,这部分是模板中最值得借鉴的工程化做法。
AppTheme:主题创建与注入
AppTheme.tsx 用React.useMemo缓存createTheme结果,关键配置为:
createTheme({ cssVariables: { colorSchemeSelector: 'data-mui-color-scheme', cssVarPrefix: 'template', }, colorSchemes, typography, shadows, shape, components: { ...inputsCustomizations, ...dataDisplayCustomizations, ...feedbackCustomizations, ...navigationCustomizations, ...surfacesCustomizations, ...themeComponents, }, });cssVariables将主题的 palette、spacing 等编译为 CSS 变量,前缀为template(即--template-palette-*等),这正是SignInContainer中--template-frame-height这类变量命名风格的来源;暗色/亮色切换通过data-mui-color-scheme选择器完成;colorSchemes是 Material UI v6 引入的颜色方案 API,用{ light: {...}, dark: {...} }一次性声明两套色板,配合useColorScheme即可运行时切换;components按类别展开五组组件级定制(inputs、dataDisplay、feedback、navigation、surfaces,分别位于 customizations 目录),把“组件样式”与“基础令牌”分离,便于按需覆盖;- 最外层用
<ThemeProvider theme={theme} disableTransitionOnChange>渲染,避免切换模式时颜色过渡动画闪烁;当disableCustomTheme为真时直接返回Fragment,不注入主题。
themePrimitives:设计令牌
themePrimitives.ts 是所有颜色的源头,定义了五组 HSL 色阶(brand、gray、green、orange、red,各含 50~900 十级,见 L33-L96),并导出:
colorSchemes(L241-L341):light/dark 两套完整 palette。以 light 为例,primary.main取brand[400](hsl(210, 98%, 42%))、contrastText取brand[50];dark 模式将primary.main提升到brand[700]等。每套色板还带一个自定义的baseShadow令牌,并被注入到shadows[1]——shadows数组将默认阴影第一项替换为'var(--template-palette-baseShadow)'(L398-L403),使全局阴影随主题变量联动;typography(L343-L391):Inter, sans-serif字体族,h1 48px/600 字重/1.2 行高,逐档定义至 caption 12px;shape:全局borderRadius: 8;getDesignTokens(mode)(L98-L239):旧式的按PaletteMode生成完整 token 的函数,与colorSchemes并存——AppTheme实际使用的是colorSchemes,而getDesignTokens为需要按模式手工取 token 的场景保留。
定制品牌色的最短路径就是改这个文件:例如把brand色阶整体换成你的主色 HSL 值,light/dark 两套colorSchemes.palette.primary会随之全部更新,模板中所有color="primary"的按钮、文字、边框都自动生效,无需逐组件修改。
ColorModeSelect:模式切换控件
ColorModeSelect.tsx 仅 27 行:调用useColorScheme()取得{ mode, setMode },渲染一个Select,选项为System/Light/Dark;若mode为空(即上层未启用 color scheme,如CssBaseline未加enableColorScheme)则返回null。SignIn组件把它fixed在右上角,形成模板预览中“右上角切换主题”的交互。
接入建议与注意事项
- 复制范围:务必同时复制
sign-in与shared-theme两个目录,并保持两者的相对位置(shared-theme与sign-in同级),否则../shared-theme/AppTheme导入会失败。 - 依赖版本前提:模板使用了 v6 的
colorSchemes配置项与slotProps属性(见AppTheme.tsx与ForgotPassword.tsx),请使用与仓库当前版本一致的@mui/material(v6 及以上);@emotion/react、@emotion/styled是sx/styled的必需 peer 依赖。 - 语言选择:每个文件均有
.js与.tsx双版本,TypeScript 项目中选.tsx版本即可;SignIn的disableCustomTheme属性是文档站预览专用,业务项目可忽略。 - 接入后端:模板中
handleSubmit只做console.log,社交按钮只是alert占位,认证接口需自行替换。 - 横向扩展:同一套
shared-theme也被 sign-in-side(带侧边宣传图的登录布局)、sign-up(注册页)等模板复用,按同样的“复制目录 + 导入组件”方式即可组合出风格统一的登录/注册/找回密码整套流程页面。
【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考