news 2026/10/2 11:35:20

Element Plus 表单必填星号完全指南:原理、动态切换与定制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Element Plus 表单必填星号完全指南:原理、动态切换与定制实战

做了这么多年后台管理系统,Element 的表单组件算是打交道最多的东西之一。早些年用 element-ui,现在切到 element plus,必填星号这个问题几乎在每个项目里都会碰到。简单说,需求无非就这几种:必填项显示红色星号、非必填项不要星号、某些字段只显示星号但不参与校验、某些字段要校验但不能让用户看到星号,还有一种是动态切换必填状态时星号要跟着变。但就这么个“小星号”,实际开发里坑还真不少,尤其是 element 内部对 required 和 rules 的自动处理机制,不摸透的话,你写了required反而校验不触发,或者明明没写required星号却自己跑出来了。

这篇文章就把 element 表单必填星号这件事从头到尾拆一遍,包含底层逻辑、不同场景的实现方式、样式定制,以及我在项目中踩过的各种坑。不管你是刚接触 element-ui 的新手,还是已经被动态表单折磨过的老手,这篇应该都能给你省点时间。

1. 先搞懂 Element 表单星号是怎么冒出来的

很多新手第一次遇到星号问题,基本都是同一个场景:我在el-form-item上只写了prop和一个输入框,啥都没配置,结果 label 前面莫名其妙多了一个红星星。一看代码,也没写required啊,这是咋回事?

这就要从 element 的源码逻辑说起。Element 的 form-item 组件内部会做一件事:解析传入的rules规则,如果检测到某条规则是必填规则(required: true),就自动给表单项标记为必填态(is-required),从而渲染出红色星号。也就是说,只要你写了类似下面这种 rules:

rules: { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }] }

form-item 就会自动识别出 username 这条规则带required: true,于是乖乖把星号渲染出来。这就是为什么你“啥都没写”却出现了星号的真相——星号的开关不只是required属性,rules里的必填规则同样能触发它。

1.1 三种触发星号的写法

Element 里能触发必填星号的常见方式有三种,我直接用代码列出来:

方式一:在 el-form-item 上使用 required 属性

<el-form-item label="用户名" prop="username" required> <el-input v-model="formData.username" placeholder="请输入用户名" /> </el-form-item>

这种方式最简单粗暴,直接在表单项上标记必填。它会让表单项带上is-required样式类,从而显示星号,同时在提交校验时把该字段作为必填项校验。不过要注意,required属性在 element-ui 和 element plus 里的行为略有差异,后面会专门讲。

方式二:通过 rules 规则定义

rules: { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }] }

这是我的主要写法。把校验规则集中放在一个对象里维护,代码更清晰,也方便动态修改。如上所说,form-item 会自动解析 rules 里的必填规则,星号也能正常显示。

方式三:同时设置 required 和 rules

<el-form-item label="用户名" prop="username" required :rules="[{ required: false }]">

这种写法会产生一些微妙的冲突,建议尽量别用。required 属性和 rules 里的 required 规则同时存在时,element 内部处理逻辑有时候会二选一,或者是先取 rules 里的规则、忽略 required 属性,或者相反,不同版本行为还不一样,容易出幺蛾子。

1.2 required 和 rules 到底什么关系

很多教程会说“required 是 rules 的简写”,这个说法在 element 里其实不完全准确。从源码角度看,form-item 在渲染时做了这样一件事:

  1. 收集自己身上的required属性。
  2. 解析外部传入的rules中的必填规则。
  3. 两者只要有一个成立,就认为这个字段需要显示星号。
  4. 如果 required 为 true,但 rules 里没有必填规则,校验时 element 会自动生成一条“必填”的校验规则参与校验。

所以说,required属性更像是“星号 + 必填校验”的组合开关,而rules里的required则只是校验规则之一,星号只是它带来的“副作用”。

理解这个机制非常关键。我在一个动态表单项目里就吃过亏:某个字段的校验规则是通过接口动态返回的,返回的规则里带required: false或者干脆没有 required 字段。正常逻辑下,这个字段不该有星号,但因为我之前给 el-form-item 加了required属性,结果星号一直挂着不消失,用户看着就很困惑。

所以我的建议是:如果你需要动态控制校验规则,尽量只用 rules,不要混用 required 属性。rules 的优先级和可维护性都会更好,后面第四节会专门讲动态控制。

2. 按需控制星号:显示、隐藏、只显示不校验

搞清楚星号的生成机制后,接下来就是各种“按需控制”的需求。这部分是实际开发里最常遇到的情况,我按场景逐个拆。

2.1 全局关闭星号:hide-required-asterisk

有时候表单设计图里就没有星号,但校验逻辑依然要保留必填。比如一些面向内部系统的录入页面,所有字段都是必填但不想让界面看起来全是红星,这时候就该用hide-required-asterisk。

在 el-form 上设置这个属性,可以全局隐藏所有必填星号:

<el-form ref="formRef" :model="formData" :rules="rules" hide-required-asterisk label-width="120px" > <el-form-item label="用户名" prop="username"> <el-input v-model="formData.username" /> </el-form-item> </el-form>

这样即使 rules 里有required: true,界面上也不会显示星号,但提交校验时必填逻辑依然生效。

Element plus 的 el-form-item 上也支持单独的hide-required-asterisk,可以只对某一个表单项隐藏星号:

<el-form-item label="用户名" prop="username" hide-required-asterisk> <el-input v-model="formData.username" /> </el-form-item>

这个属性在 element-ui 里没有,是 element plus 新增的。如果你还在维护旧版的 element-ui 项目,只能通过给当前表单项动态添加一个类名,再用 CSS 隐藏伪元素,或者用下面的自定义 label 方案。

2.2 只显示星号但不校验的玩法

有很多场景需要“视觉上的必填感”,但业务上并不强制。比如某个字段建议填写,填了更有利于审核通过,但不填也能提交。产品经理就会说:“这个字段加个星号吧,让用户知道建议填。”这时候如果直接写required,提交就直接被拦截了,根本不符合业务预期。

Element plus 提供了一个非常贴心的属性:asterisk。它只控制星号显示,不参与任何校验:

<el-form-item label="手机号" prop="phone" asterisk> <el-input v-model="formData.phone" placeholder="选填,但建议填写" /> </el-form-item>

这样界面上会显示红色星号,但提交时不会有必填校验拦截。

如果你是 element-ui 老版本用户,没有asterisk属性,可以用自定义 label + CSS 伪元素实现。比如:

<el-form-item label=" " prop="phone"> <template #label> <span class="required-star-label">手机号</span> </template> <el-input v-model="formData.phone" placeholder="选填,但建议填写" /> </el-form-item>
.required-star-label::before { content: '*'; color: #f56c6c; margin-right: 4px; }

既然 label 是自定义的,星号位置、颜色你都能自己控制。这个方案还有个额外好处:在 element-ui 和 element plus 里都能用,算是一个通用的兜底方案。

2.3 有校验但看不见星号怎么办

反向需求也有:字段确实必填,但设计要求这里不能出现星号,甚至是这几个字段整体不能出现星号。比如密码重置页面,三个输入框(原密码、新密码、确认密码)全部必填,但页面设计为了简洁美观,不想显示任何星号。

这样处理:

<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" > <el-form-item label="原密码" prop="oldPassword" hide-required-asterisk> <el-input v-model="formData.oldPassword" type="password" show-password /> </el-form-item> <el-form-item label="新密码" prop="newPassword" hide-required-asterisk> <el-input v-model="formData.newPassword" type="password" show-password /> </el-form-item> <el-form-item label="确认密码" prop="confirmPassword" hide-required-asterisk> <el-input v-model="formData.confirmPassword" type="password" show-password /> </el-form-item> </el-form>

rules 里正常写required: true,校验依然生效,只是三个星号全都不显示了。注意,el-form 和 el-form-item 都支持hide-required-asterisk,并且表单项自己的配置优先级更高,可以实现局部控制。

3. 定制星号颜色、位置和样式

星号默认是红色,位置在 label 文字左侧(::before伪元素)。但 UI 设计往往不会这么乖,今天要蓝色,明天要粗体,后天要星号跑到右边去。好在这些都是纯 CSS 能解决的问题。

3.1 用 CSS 变量改星号颜色

Element plus 的星号颜色用的是主题变量--el-color-danger,默认是#f56c6c。如果只是想让所有表单星号变个颜色,最简单的做法是覆盖这个变量:

:root { --el-color-danger: #fa9d3b; /* 改成橙色 */ }

但要注意,--el-color-danger是全局的,它会连带影响其他用到 danger 色值的组件(比如消息提示、表单校验报错文字等)。如果你只想改星号颜色,不想影响其他组件,更精准的做法是直接给星号伪元素写样式:

.el-form-item.is-required .el-form-item__label::before { color: #fa9d3b !important; }

Element plus 里星号的 DOM 结构是 el-form-item__label 上的::before伪元素,选择器写到这个层级即可。注意加!important,因为 Element 自带样式的优先级不低,不加会被覆盖掉。

3.2 把必填星号挪到 label 右侧

国内表单多数习惯把星号放在 label 文字左侧,英文系统则相反,星号通常放右侧。Element 默认是左侧,想变右侧就这么办:

.el-form-item.is-required .el-form-item__label::before { content: '' !important; display: none; } .el-form-item.is-required .el-form-item__label::after { content: '*' !important; color: #f56c6c; margin-left: 4px; display: inline-block; }

思路很简单:干掉左侧的::before,在::after里重新画一个星号。这个方案在 element-ui 和 element plus 的较新版本里都验证过,基本不会出问题。

不过有一个细节要注意:如果 label 文字本身可以换行,或者 label-width 设置得不够宽,星号跑到右侧后可能看起来跟下一个字段挨得太近,视觉上容易误读。所以调整星号位置后,最好检查一下整列 label 的对齐效果。

3.3 label-width 不够导致星号换行的坑

这个问题特别隐蔽。当 label 文字比较长,比如“自定义数据源名称”这种 8 个字的 label,而 label-width 设的是 100px 时,label 内容加上左侧星号后可能会被挤到第二行。表现出来就是星号在第一行、文字在第二行,或者文字被截断。

解决思路有三个:

  1. 调大 label-width,例如label-width="130px"。
  2. 使用label-position="top",让 label 和输入框上下排布,星号换行问题天然消失。
  3. 如果 label 长度不固定,干脆用label-width="auto"(element plus 支持,element-ui 部分版本也支持),让 element 自动计算 label 宽度。

第三种方案在动态表单里最实用。因为动态表单的字段名经常来自后端配置,长度不可控,设置固定 label-width 很容易踩坑。用 auto 之后,由组件自己适配,省心不少。

4. 动态表单、联动校验与星号同步

动态表单大概是星号问题的高发区。比如表单引擎、低代码平台,字段配置从后端 JSON 拉下来,然后前端动态渲染。这时候星号必须跟着字段配置走,配置说必填就显示,配置说不必填就消失,而且切换过程还得顺滑。

4.1 动态配置驱动的星号渲染

假设后端返回的字段配置长这样:

const fields = [ { prop: 'name', label: '姓名', required: true }, { prop: 'age', label: '年龄', required: false }, { prop: 'email', label: '邮箱', required: true, pattern: 'email' } ];

渲染时动态绑定required和rules:

<el-form :model="formData" :rules="dynamicRules" label-width="130px"> <el-form-item v-for="field in fields" :key="field.prop" :label="field.label" :prop="field.prop" :required="field.required" > <el-input v-model="formData[field.prop]" /> </el-form-item> </el-form>
const dynamicRules = computed(() => { const rules = {}; fields.forEach((field) => { if (field.required) { rules[field.prop] = [{ required: true, message: `请输入${field.label}`, trigger: 'blur' }]; } }); return rules; });

这种写法同时绑定了required和rules,从逻辑上看是重复了。但实际测试下来,只要required和 rules 里的必填规则保持“同真同假”,一般不会出问题。我建议保持两者状态一致,别一个 true 一个 false。

4.2 联动场景:A 选了 B 才必填

比单纯的动态渲染更复杂的是联动校验。最常见的场景:用户选择了“是否企业用户”为“是”时,“企业名称”才必填;选择“否”时,企业名称不仅不能必填,最好还把用户之前填的内容清空,避免数据残留。

处理方式是这样:

<el-form-item label="是否企业用户"> <el-radio-group v-model="formData.isCompany"> <el-radio :value="true">是</el-radio> <el-radio :value="false">否</el-radio> </el-radio-group> </el-form-item> <el-form-item label="企业名称" prop="companyName" :required="formData.isCompany"> <el-input v-model="formData.companyName" :disabled="!formData.isCompany" /> </el-form-item>
const rules = computed(() => { const result = {}; if (formData.isCompany) { result.companyName = [{ required: true, message: '请输入企业名称', trigger: 'blur' }]; } return result; });

注意这里有个很实际的坑:如果之前已经因为必填校验报过错,再把isCompany切回false时,即使 rules 里已经没有 companyName 的必填规则了,界面上那条“请输入企业名称”的报错信息可能还在。这时候需要手动调用clearValidate清掉:

watch(() => formData.isCompany, (val) => { if (!val) { formRef.value.clearValidate('companyName'); } });

4.3 更新 rules 后星号不同步的解决方法

动态修改 rules 后,星号有时候不会立刻跟着变。这是我在实际项目里遇到的另一个经典问题。原因是 el-form-item 对 rules 的解析结果有缓存,或者说是响应式依赖追踪的时机问题。

遇到这种情况,最简单的解决办法是给 el-form-item 加一个动态的key,强制它重新渲染:

<el-form-item :key="field.prop + '-' + field.required" :label="field.label" :prop="field.prop" :required="field.required" > <el-input v-model="formData[field.prop]" /> </el-form-item>

key 一旦变化,Vue 会销毁旧组件、创建新组件,星号和校验状态自然全部刷新。这是最保险的做法,唯一的代价是组件重建会导致输入框内容暂时丢失,所以只推荐在字段配置本身变化时用。

如果只是星号显示不同步,但输入值不能丢,那可以不重建组件,改用下一节里的 CSS 方案强制刷新显示状态,或者检查一下是不是 form-item 的 required 属性绑定值没有正确响应。

5. 常见问题与排查实录

这部分整理我在实际开发中真正踩过的坑,每个都有具体场景和解决方案。如果你正准备交付表单功能,建议把这节通读一遍,能帮你少加好几个班。

5.1 必填校验没生效但星号还在

现象:星号正常显示,但提交表单时空值也能通过校验,或者必填校验压根没触发。

排查思路:

  1. 先看 el-form 上有没有写:model,并且 el-form-item 上有没有写prop。缺一个,校验都找不到字段。
  2. 再看 el-input 上有没有写v-model,并且绑定的属性名和 prop 是否一致。不一致的话,校验作用在 A 字段,用户输入在 B 字段,永远校验不出问题。
  3. 最后检查 el-form 的rules是不是写在子组件里了。很多人在子组件里定义 rules,但 el-form 在父组件里,导致 rules 没传进去。这种情况下星号有时能显示、校验却不生效,非常坑。

星号显示但校验不生效,根源往往是“星号是手动 required 强行显示的,但 prop 或 rules 没配对”。最简单的验证方法:把 el-form-item 的 required 属性去掉,只留 rules,看星号是否还在。如果星号消失了,说明 rules 里的必填规则没有传递到该表单项,重点检查 prop 是否匹配。

5.2 resetFields 和 clearValidate 用不对

先说结论:

  • resetFields:把整个表单重置到初始值(不是清空!),并移除所有校验状态。
  • clearValidate:只移除校验状态(报错信息),不改变值。

很多新手的误区是把 resetFields 当“清空表单”用。比如用户点“新增”按钮,期望把表单变成全空,然后调用了 resetFields,结果发现表单里还有上一次的数据。原因就是 resetFields 是把字段重置为表单挂载时的初始值,而不是空值。如果初始值是{ name: '张三' },reset 之后 name 还是张三。

要真正清空表单,需要手动把 model 里的所有字段置空,再调用 clearValidate:

function resetForm() { Object.assign(formData, { name: '', age: null, email: '' }); formRef.value.clearValidate(); }

还有一点:resetFields 只能重置在 el-form-item 上配置了 prop 的字段。没配 prop 的字段不在表单校验体系里,resetFields 管不着。

5.3 el-date-picker 时间范围校验怎么写

热搜词里有一条“el-date-picker判断结束时间大约起始时间”,应该就是开始日期和结束日期的校验问题。这里的难点是:既要限制用户选择范围(日期选择器上禁用不合法日期),又要在提交时校验结束时间大于开始时间。

我常用的写法是自定义 validator:

<el-form-item label="开始日期" prop="startDate"> <el-date-picker v-model="formData.startDate" type="date" placeholder="请选择开始日期" :disabled-date="disabledStartDate" /> </el-form-item> <el-form-item label="结束日期" prop="endDate"> <el-date-picker v-model="formData.endDate" type="date" placeholder="请选择结束日期" :disabled-date="disabledEndDate" /> </el-form-item>
const disabledStartDate = (date) => { if (!formData.endDate) return false; return date.getTime() > new Date(formData.endDate).getTime(); }; const disabledEndDate = (date) => { if (!formData.startDate) return false; return date.getTime() < new Date(formData.startDate).getTime(); };

校验规则里再加一个自定义校验器:

const validateDateRange = (rule, value, callback) => { if (!formData.startDate || !formData.endDate) { callback(); return; } if (formData.endDate < formData.startDate) { callback(new Error('结束日期不能早于开始日期')); } else { callback(); } };

两个角度双保险:用户选择阶段用 disabled-date 限制可选范围,提交阶段用 validator 兜底。disabled-date 只能防止用户点击不合法日期,但程序赋值或初始化时可能绕过它,所以 validator 不能省。

5.4 element plus 表格偶发阴影问题

这个跟表单星号关系不大,但既然热搜词里提到了 element plus 2.11.4 版本表格偶尔出现莫名阴影,我就顺带说一句。这类问题多半是固定列(fixed)在横向滚动时产生的阴影残留,属于组件样式 bug。排查顺序是:先看是不是固定列阴影,再试去掉 fixed 属性看是否复现,最后确认版本号,看官方 changelog 有没有修复。如果项目锁定了版本,紧急处理方式是用 CSS 覆盖固定列阴影的 z-index 或背景色。

5.5 前端校验不是全部:后端校验不可省

最后想提醒一句,Element 表单校验做得再好,也只是客户端的体验优化,不能作为数据安全的唯一防线。之前遇到过一个表单提交场景,前端明明校验了必填,结果接口直接拿过去,有人绕过页面调用接口,照样能把空数据写进数据库。后来检查下来就是后端接口没做必填校验,被当作“存储型 XSS 的风险入口”通报了。所以前端校验负责体验,后端校验负责安全,两者不能互相替代。必填、格式、长度这些规则,后端接口上该判还得判。

6. 我的一些实操心得

做了这么多表单相关的功能,我个人的体会是:Element 的星号机制不复杂,但它的触发源不止一个,导致很多人对它的理解停留在“玄学”层面。其实只要记住两件事,大部分问题都能解决:

一是星号本质是 is-required 样式类渲染出来的伪元素,凡是能让表单项进入 required 状态的操作,都会显示星号。二是校验逻辑和星号显示是可以解耦的,一个管视觉,一个管提交,两者在复杂业务里不一定同步,需要你根据场景主动控制。

最后再分享一个小技巧:如果你的表单是“绝大部分必填、极个别选填”,可以考虑用hide-required-asterisk全局隐藏星号,然后在选填字段上自定义一个“选填”标签。这样做的好处是界面干净,同时暗示用户大部分字段都要填,比满屏红星更优雅,也是在真实项目里被验证过体验不错的方案。

这段代码不算多,但你真正用到动态表单、联动校验的时候,就会明白掌握星号背后那套机制,比死记几个属性值重要得多。表格呈阴影、时间校验这些看上去无关的坑,其实都和一个核心问题挂钩:Element 组件不是单纯的“标签 + 输入框”,它内部有自己的状态逻辑,只有理解了这些逻辑,才能在边界场景里少走弯路。希望这篇文章能给你在表单星球上省下一点排查的时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 11:32:08

视频画面文字提取全流程:从抽帧到结构化输出的工程实践

1. 视频画面文字提取的整体设计思路1.1 为什么视频OCR比图片OCR难很多人第一次接触OCR&#xff0c;都是从一张清晰的截图或者扫描件开始的&#xff0c;觉得识别率挺高&#xff0c;就以为视频画面提取文字也是同样的套路。实际做过一个完整项目之后你会发现&#xff0c;视频OCR的…

作者头像 李华
网站建设 2026/10/2 11:30:15

Jev 深度解析:TypeSafe AI 与 System One Model 的本地部署与 SDK 接入指南

1. 从热搜词里读懂 Jev 的真实定位1.1 为什么“Jev”突然被这么多人搜最近一段时间&#xff0c;不管是在技术社区、开发者群&#xff0c;还是在做 AI 应用的小圈子里&#xff0c;“Jev”这个词出现的频率明显高了起来。很多人第一次看到它&#xff0c;脑子里冒出的第一个问题就…

作者头像 李华
网站建设 2026/10/2 11:29:56

openrig开放机架:高密度GPU计算平台的搭建与运维实践

1. openrig这个标签背后&#xff0c;是一整套“开放机架”的设计哲学第一次看到贴着“OPENRIG”标签的设备&#xff0c;是在一个做渲染计算的朋友团队那里。远远看去&#xff0c;那台机器没有机箱外壳&#xff0c;铝合金框架里整整齐齐排着八张显卡&#xff0c;电源挂在机架侧边…

作者头像 李华
网站建设 2026/10/2 11:29:56

MIUI 12稳定版ADB权限机制深度解析与适配实践

1. 这不是“破解”&#xff0c;而是对MIUI 12稳定版系统逻辑的重新理解很多人一看到“MIUI开发者选项限制解除”&#xff0c;第一反应就是找什么隐藏代码、刷机包&#xff0c;或者下载一堆来路不明的ADB工具合集。我去年在给三台不同型号的小米手机&#xff08;Redmi K30 Pro、…

作者头像 李华
网站建设 2026/10/2 11:29:47

从零手把手DIY一台OpenRig开放式硬件测试平台

1. OpenRig到底是什么&#xff0c;我为什么折腾了这么一台"裸架" OpenRig往简单里说&#xff0c;就是一台开放式PC硬件测试平台——没有侧板、没有传统机箱结构&#xff0c;主板直接平放或者竖挂在铝合金框架上&#xff0c;电源、显卡、散热器全部露天安装。听起来像…

作者头像 李华
网站建设 2026/10/2 11:28:59

前端秒杀自动化:状态驱动的网页抢购JS方案

1. 这不是“黑产脚本”&#xff0c;而是一套可验证、可调试、可审计的前端自动化交互方案 “利用 JS 脚本实现网页全自动秒杀抢购”——这个标题在技术社区里常被误读为“外挂”或“刷单工具”&#xff0c;但作为从业十年、亲手交付过7个高并发电商系统前端架构的工程师&#x…

作者头像 李华