资讯动态

Element表单必填星号全解:从原理到动态校验的避坑指南

发布时间:2026/10/2 11:35:21 来源:尧图企业网站定制
做后台管理系统的人几乎每天都在跟Element表单打交道。刚入行那会儿我也觉得给表单加个必填星号不就是一行代码的事直到后来被产品经理追着改了一次又一次——有的是星号位置不对有的是必填项没触发校验还有的是动态表单里星号时有时无。今天这篇就把我这些年折腾Element表单必填星号的经验整理出来从最简单的写法到复杂的动态校验从Element UI到Element Plus一次性说透。无论你是刚接触Vue生态的新手还是已经维护了好几个后台项目的老人这篇文章都能给你一点参考。我会先讲清楚星号的底层原理再对比几种不同的实现方式然后把日常开发里最容易踩的坑和排查思路列出来最后附上一些高阶用法比如自定义星号样式、动态切换必填、日期范围校验这类让产品经理满意的细节操作。1. Element表单必填星号的底层原理与实现方式1.1 星号是怎么渲染出来的很多同学用了很久的Element表单却不知道那个红色星号到底从哪来。其实它就是一个CSS伪元素Element UI和Element Plus在源码里都干了同一件事匹配带有必填标记的el-form-item然后在label前面插入一个*字符。具体来说Element UI的实现是给el-form-item加上is-required类名然后通过.el-form-item.is-required .el-form-item__label::before这个伪元素来显示星号。Element Plus从2.x版本开始改动比较大它提供了一个独立的require-marker属性来控制星号的渲染同时保留了CSS伪元素的兼容方案。明白这个原理之后很多问题就能解释了为什么rules里配了required: true星号自动出现了因为Element检测到校验规则中有必填项会自动给对应的el-form-item加上is-required类。为什么用了自定义slot或者自定义label之后星号消失了因为伪元素选择器要求label结构是原生的el-form-item__label你一旦自定义了内容DOM结构变了原本的伪元素自然就不生效。为什么某些版本的Element Plus星号不见了因为新版默认使用了require-marker逻辑如果你在el-config-provider里把这个属性全局设成了false或者组件上显式写了:require-markerfalse星号就会被强行关掉。理解了底层机制后面所有关于星号的骚操作都能顺藤摸瓜找到入口。1.2 三种最常用的加星号方式实际开发中给表单加星号大致有三种写法各有适用场景这里直接对比一下。实现方式是否显示星号是否触发校验适用场景el-form-item上直接写required属性是否只做视觉提示不参与提交校验rules中配置required: true是是常规必填校验最推荐自定义label内容手动加*是取决于rules配置特殊排版需求比如星号要在label中间第一种方式很简单在el-form-item上写一个required属性就行。但这里有个隐藏的坑这个required属性只负责显示星号它本身不参与校验逻辑。也就是说用户不填这个字段直接提交表单不会报错。如果只是UI上想标个星号提交逻辑另外处理那可以用这种方式否则还是老老实实用rules。第二种方式是目前项目里最常用的在rules里配置校验规则rules: { name: [ { required: true, message: 请输入姓名, trigger: blur } ] }Element会自动解析这条规则检测到required: true后给对应的el-form-item加上星号同时提交前校验也会生效。这是星号显示和必填校验联动最标准的方式。第三种方式比较特殊多见于自定义表单项。有时候需求方要求星号放在label文字中间或者label是一个带图标的富文本这时候原生星号就不太够用了。处理办法是在slot自定义的label里手动加一个红色星号节点同时rules里依然保留required: true这样既能满足视觉需求又不丢失校验逻辑。2. 从Element UI到Element Plus版本差异与写法迁移2.1 Vue2和Vue3组件库的星号机制差异Element UI是Vue2生态的组件库Element Plus是Vue3生态的升级版。这两个库在表单星号的实现上差别不小如果你是老项目升级这里面的坑建议提前摸清楚。Element UI的星号完全依赖CSS伪元素is-required类名一加星号就跑出来了。它没有一个独立的属性去控制星号的显示和隐藏。这意味着某天你不想显示星号但又想保留校验规则时只能靠覆盖CSS来解决操作起来有点绕。Element Plus在2.x版本引入了require-marker属性这个属性专门用来控制必填星号是否展示。默认值是true也就是规则里有必填项就显示。如果你希望校验还是要校验但UI上不显示星号直接设成false就行了比Element UI时代优雅很多。除了属性层面的变化Element Plus还把校验规则引擎升级成了async-validator的4.x版本部分错误提示的trigger逻辑跟老版本有细微差异。切版本的时候最好把涉及表单校验的用例全部回归一遍尤其是那些同时用了blur和change触发器的场景。这里还要提一个Element Plus的版本小插曲。2.11.4版本刚发布那阵子不少人在GitHub上反馈表格组件偶尔会出现来路不明的阴影排查半天发现是样式层的问题刷新页面又好了。如果你正好踩到这个先别怀疑自己的代码去翻一下版本发布记录升级到修复版本往往就解决了。2.2 必填星号相关的配置项详解既然说到了Element Plus这里把跟星号相关的配置项集中整理一遍免得每次都要翻文档。第一个是el-config-provider里的全局配置。如果你希望整个项目的必填星号统一隐藏可以在应用入口配置el-config-provider :require-markerfalse App / /el-config-provider这样所有表单项的星号都会隐藏但校验规则不受影响。这个全局配置对Element Plus 2.x以上版本生效Element UI没有这个能力。第二个是el-form-item上的require-marker属性。它的优先级比全局配置高适合某些表单页需要单独开/关星号的场景el-form-item label姓名 propname :require-markerfalse el-input v-modelform.name / /el-form-item第三个是label-position属性。当表单的label在顶部label-positiontop时星号默认跟label在同一行位置相对自然当label在左侧默认的right或left时星号和label文字之间的间距由CSS控制。Element UI和Element Plus的默认间距略有差异升级版本后如果发现星号贴住了label文字检查一下是否有自己覆盖的样式。2.3 动态表单与表单引擎场景下的星号处理现在很多中后台项目都会封装自己的表单引擎或者直接用若依这类脚手架自带的表单设计器。在这种动态场景下星号的处理逻辑就不能写死了。最常见的做法是通过配置项驱动。表单的字段配置里维护一个required字段渲染的时候根据它动态生成rulesconst fieldConfig [ { prop: name, label: 姓名, required: true }, { prop: age, label: 年龄, required: false } ] const buildRules (field) { return field.required ? [{ required: true, message: 请输入${field.label}, trigger: blur }] : [] }渲染时用v-for遍历配置el-form-item v-forfield in fieldConfig :keyfield.prop :labelfield.label :propfield.prop :rulesbuildRules(field) component :isfield.component v-modelform[field.prop] / /el-form-item这种写法的好处是当后端下发动态表单配置时只需要改配置数据就能控制星号和校验规则的显隐不需要动模板代码。但要注意如果你的表单设计器支持动态执行脚本比如在某个字段值变化后动态改变另一个字段的必填状态那就必须保证改变required和重新校验是同步的。我的习惯是在脚本里同时操作配置值和clearValidatefieldConfig[1].required true nextTick(() { formRef.value.clearValidate(age) })不然经常会出现一种诡异的情况星号已经亮了但提交时校验规则还是旧的没有拦截住空值。3. 进阶技巧自定义星号、必填校验与交互细节3.1 自定义星号的颜色、位置和显示逻辑产品经理的审美千奇百怪红色星号并不总是能满足需求。有的要橙色星号有的要星号变成必填两个字还有的想让星号闪烁。这些问题都可以通过覆写CSS或改造label结构来解决。先说最简单的颜色和大小修改。Element UI和Element Plus的星号都是伪元素渲染的直接覆写样式就能生效.el-form-item.is-required .el-form-item__label::before { color: #ff7d00; font-size: 16px; }Element Plus如果用的默认require-markerDOM结构和老版本不一样覆写时优先选择.el-form-item__label上的相关类。如果你担心选择器优先级问题建议加一个自定义class作为作用域.required-orange .el-form-item__label::before { color: #ff7d00; }然后在需要特殊颜色的el-form-item上加上这个class。再说把星号换成文字的场景。比如标签后面跟着一个灰色的小字必填这个直接用CSS伪元素可以做但更稳妥的办法是自定义labelel-form-item propname template #label span姓名/span span classrequired-tag必填/span /template el-input v-modelform.name / /el-form-item注意一旦走了#label的slot原本基于el-form-item__label的伪元素样式就不起作用了所以必填两个字要用自己的样式来写。这不算坑只是容易忽略很多人自定义完label才发现星号不见了就是这个原因。3.2 必填星号和校验规则的高阶配合必填星号只是视觉表达真正干活的还是校验规则。这里分享几个比较有代表性的配合场景。第一个是数组类型的必填校验。比如一个字段需要至少上传一张图片、至少选一个标签这种场景单纯写required: true是校验不出数组长度的。正确的写法是rules: { tags: [ { type: array, required: true, message: 请至少选择一个标签, trigger: change } ] }注意trigger: change数组类型的值变化走的是change事件用blur可能失效或者触发不及时。第二个是自定义validator实现联动必填。比如某个字段只有在另一个字段有值时才必填这时候不能用静态的required要写自定义函数const validatePhone (rule, value, callback) { if (form.contactWay phone !value) { callback(new Error(请填写手机号)) } else { callback() } }映射到rules里就是rules: { phone: [ { validator: validatePhone, trigger: blur } ] }在联动场景里星号的显示其实并不会自动跟随规则变化——静态的required: true会让星号一直显示哪怕当前其实不需要必填。我的做法是用计算属性动态生成rules同时用require-marker配合判断el-form-item label手机号 propphone :require-markerform.contactWay phone :rulesform.contactWay phone ? phoneRequiredRule : [] 这样星号和校验规则就联动起来了体验上比星号常亮但规则时而生效时而不生效要好得多。第三个是日期范围的校验。热门问题里经常有人问el-date-picker怎么判断结束时间大于起始时间核心依然是自定义validatorconst validateDateRange (rule, value, callback) { if (!form.startDate || !form.endDate) { callback() return } if (form.endDate form.startDate) { callback(new Error(结束时间不能早于开始时间)) } else { callback() } }这里有一个小细节日期比较建议先用时间戳转换避免不同格式字符串比较出问题。比如new Date(form.endDate).getTime() new Date(form.startDate).getTime()这样在处理2025-03-10和2025-3-1这类格式不统一的值时更稳。3.3 表单回显、清空内容与星号状态同步开发编辑页的时候常常会遇到表单回显后星号和校验状态不同步的问题。常见场景是数据已经回显了但单击提交依然提示某个字段必填或者清空表单后之前的校验错误还在。清空表单内容这里必须分清两个API。resetFields()会把表单值重置为初始值同时清除校验状态而clearValidate()只清除校验状态不重置值。如果你的需求是用户点了重置按钮所有字段恢复默认可以用resetFields如果只是提交成功后清空输入框但不要改变默认值那要结合具体业务来定。我在实际项目里踩过的坑是在Vue3 Element Plus中resetFields有时候没能正确清除校验状态。排查下来发现原因是el-form-item上的prop和el-form的model里的字段名不一致。这个很隐蔽比如model里是user.name而prop写的name组件内部匹配不到resetFields就处理不了这个字段。解决办法是保证prop是model字段的完整路径尤其在嵌套对象时要注意。清空表单还有一个常见做法是遍历模型手动置空Object.keys(form).forEach(key { form[key] undefined }) nextTick(() { formRef.value.clearValidate() })这种方式干净利落不会触发reserveFields的怪异逻辑但缺点是如果表单里有非表单项的数据比如id也会一并清掉。所以更严谨的写法是在初始值里维护一个defaultForm对象重置时直接浅拷贝。4. 常见问题排查与避坑实录4.1 为什么rules配置了但星号不显示这个问题在社群里出现的频率极高。明明rules里写了required: true页面上的label就是没有星号。我总结了一圈无非是下面几个原因。第一个是prop没写或者写错了。el-form-item的prop是连接校验规则和表单数据的桥梁它必须和rules里的字段名一致。你写propnamerules里也得有name这一项否则组件压根找不到校验规则自然不会显示星号。第二个是el-form上没绑定:rules或者model。很多新手会把rules写在el-form-item上但Element组件库的规则优先级和查找链路是依赖el-form的model和rules的。如果父级没绑定子级的光杆司令式规则可能不生效。第三个是自定义label导致伪元素失效。这一点上面提过用了#label插槽之后原本的.el-form-item__label::before不会渲染因为DOM被替换了。解决方案是手动在label模板里加星号或者用CSS选择器匹配自定义内容。第四个是Element Plus的require-marker被全局设置成了false。这个坑比较隐蔽尤其是团队项目里有人为了某个特殊页面改了全局配置结果所有页面的星号都不见了。建议项目里不要轻易动el-config-provider的require-marker默认值真有必要就按页面按需覆盖。4.2 清空表单与重置校验的常见误区关于清空表单内容很多人习惯用this.$refs.formRef.resetFields()但这个方法有个前提条件el-form的model必须在mounted之前就已经挂载了初始值。如果你在mounted之后异步赋值resetFields时拿到的初始值可能是空的表单一重置之前回显的数据全没了。更稳妥的做法是在数据声明阶段就把初始数据结构准备好const form reactive({ name: , age: undefined, tags: [] })这样无论后面怎么改只要调用resetFields它就能把字段值恢复到最开始的空状态。如果你连初始结构都懒得写也可以考虑手动重置加clearValidate的组合方式。这里再补充一个细节clearValidate支持传参你可以只清空某一个字段的校验状态而不影响其他字段。比如某个字段联动变化后前一个字段的错误提示还在就可以调用formRef.clearValidate(name)定向清除。这个方法不传参时清空所有字段传参支持字符串和数组。4.3 其他高频Element问题速查整理几个跟表单、表格相关的踩坑记录不一定全是星号问题但都是后台开发里特别容易遇到的。第一个是表格复选框保留勾选的问题。很多人问到selection-change事件怎么保留上一页的勾选状态核心就是给el-table加row-key然后给el-table-column的typeselection配置reserve-selectionel-table :datatableData row-keyid el-table-column typeselection reserve-selection / /el-table记住row-key必须指向一条数据里的唯一字段通常是主键id。第二个是Element Plus 2.11.4版本表格偶尔出现阴影的问题。这个属于组件库自身的样式bug跟业务代码没多大关系。如果项目里正好锁在这个版本可以试着给表格外层加一个overflow: hidden或者升级到修复版本。遇到这种情况先确认是不是库的问题再去动业务样式不然容易白折腾。第三个是存储型XSS问题。在动态表单里如果后端下发的字段文案或者脚本会被前端直接渲染一定要对内容做转义。最好是后端存的时候就做白名单校验前端再兜底转义一次不要把用户输入直接拼进模板或者用v-html强行渲染。安全这块不能偷懒出一次事故就是大事故。4.4 结束时间大于起始时间的完整校验示例最后把el-date-picker的时间范围完整示例贴出来方便直接拿过去用el-form-item label开始时间 propstartDate el-date-picker v-modelform.startDate typedate placeholder选择开始日期 changehandleDateChange / /el-form-item el-form-item label结束时间 propendDate el-date-picker v-modelform.endDate typedate placeholder选择结束日期 / /el-form-item校验规则const validateEndDate (rule, value, callback) { const start new Date(form.startDate).getTime() const end new Date(value).getTime() if (!value) { callback(new Error(请选择结束时间)) } else if (form.startDate end start) { callback(new Error(结束时间不能早于开始时间)) } else { callback() } } rules: { startDate: [ { required: true, message: 请选择开始时间, trigger: change } ], endDate: [ { required: true, message: 请选择结束时间, trigger: change }, { validator: validateEndDate, trigger: change } ] }一个小技巧如果日期时间格式中有HH:mm:ss用new Date(value)转换时不同浏览器对字符串的解析可能会有差异。稳妥的做法是自己写一个格式化函数把日期字符串拆成年月日然后new Date避免踩浏览器兼容的坑。5. 从表单封装到低代码引擎星号之外的思考如果你只是给几个页面加加星号那看到上面基本够了。但如果你所在团队已经有了一套表单引擎那其实可以借此机会思考一下星号背后的表单模型设计。低代码表单设计器里的必填本质上不是一个UI标记而是一等公民配置项。它关系到字段校验、提交拦截、错误提示、星号显隐、甚至联动逻辑。很多现成的脚手架比如若依的表单设计器已经把这块做成了可视化配置。但可视化配置的底子依然是规则引擎字段配置项输出什么结构前端就渲染什么结构。你手动写表单的时候掌握了star和rules的关系去看那些设计器产生的JSON配置几乎一眼就能明白它为什么那样组织。我的建议是不管你是自己封装表单组件还是在用现成的表单引擎都尽量往配置驱动的方向靠。字段的完整配置包括prop、label、component、rules、requireMarker、联动条件等集中维护在一个配置数组里页面模板只负责渲染。这样项目大了之后改某个字段的必填状态、星号显隐甚至整个字段的展示顺序都只是改配置的问题不用翻模板。举个例子一个简单的配置项可能长这样{ prop: username, label: 用户名, component: el-input, required: true, rules: [], requireMarker: true, disabled: false, visible: true }渲染的时候required为true就把对应规则追加进去requireMarker为false就隐藏星号visible为false就不渲染这一项。这套机制做起来初期有一定成本但长期维护的收益非常明显。再绕回星号本身。有人说一个星号而已有必要这么啰嗦吗我的回答是小细节往往是排查大问题的入口。你搞懂了星号是怎么渲染的、校验规则是怎么关联的、resetFields和clearValidate的边界在哪很多看似诡异的表单交互问题其实都能顺藤摸瓜找到答案。实际项目中我见到的表单bug有一大半都不是组件库的锅而是对规则-显示-校验-清空这条链路理解不够透彻。希望这篇整理能帮你少走一点弯路。下次再遇到星号不显示、校验不触发、清空不彻底的问题可以先对照着排查一遍大概率能省下半天时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑