Vue 3 组件 v-model 完全指南(五):修饰符使用完全指南——内置修饰符与自定义修饰符详解
扫描 关注或者微信搜一搜:编程智域 前端至全栈交流与成长
1. 内置修饰符详解
<!-- LazyModifier.vue - .lazy 修饰符 -->
<template>
<div class="lazy-modifier">
<h3>.lazy 修饰符示例</h3>
<div class="input-group">
<label>默认(实时同步):</label>
<input v-model="defaultValue" placeholder="实时同步" />
<p>值:</p>
</div>
<div class="input-group">
<label>使用 .lazy(失焦同步):</label>
<input v-model.lazy="lazyValue" placeholder="失焦时同步" />
<p>值:</p>
</div>
<div class="comparison">
<h4>对比说明:</h4>
<ul>
<li>默认:每次输入都触发 update</li>
<li>.lazy:输入框失焦或按回车时才触发 update</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const defaultValue = ref("");
const lazyValue = ref("");
</script>
<style scoped>
.lazy-modifier {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
color: #333;
}
.input-group {
margin-bottom: 24px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.input-group input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 14px;
}
.input-group input:focus {
outline: none;
border-color: #42b883;
}
.input-group p {
margin-top: 8px;
color: #666;
font-size: 14px;
}
.comparison {
margin-top: 24px;
padding: 16px;
background: #f5f5f5;
border-radius: 6px;
}
.comparison h4 {
margin-top: 0;
color: #666;
}
.comparison ul {
margin: 0;
padding-left: 20px;
}
.comparison li {
margin: 8px 0;
color: #333;
}
</style>1.2 .number 修饰符
<!-- NumberModifier.vue - .number 修饰符 -->
<template>
<div class="number-modifier">
<h3>.number 修饰符示例</h3>
<div class="input-group">
<label>不使用 .number:</label>
<input v-model="stringValue" type="number" placeholder="字符串类型" />
<p>值: (类型:)</p>
</div>
<div class="input-group">
<label>使用 .number:</label>
<input
v-model.number="numberValue"
type="number"
placeholder="数字类型"
/>
<p>值: (类型:)</p>
</div>
<div class="calculation">
<h4>计算示例:</h4>
<p>字符串相加: + 1 = 1</p>
<p>数字相加: + 1 = </p>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const stringValue = ref("");
const numberValue = ref("");
</script>
<style scoped>
.number-modifier {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
}
.input-group {
margin-bottom: 24px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.input-group input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
}
.input-group p {
margin-top: 8px;
color: #666;
font-size: 14px;
}
.calculation {
padding: 16px;
background: #f5f5f5;
border-radius: 6px;
}
.calculation h4 {
margin-top: 0;
color: #666;
}
.calculation p {
margin: 8px 0;
font-family: monospace;
background: #282c34;
color: #abb2bf;
padding: 8px;
border-radius: 4px;
}
</style>1.3 .trim 修饰符
<!-- TrimModifier.vue - .trim 修饰符 -->
<template>
<div class="trim-modifier">
<h3>.trim 修饰符示例</h3>
<div class="input-group">
<label>不使用 .trim:</label>
<input v-model="untrimmed" placeholder="输入带空格的文本" />
<p>值:"" (长度:)</p>
</div>
<div class="input-group">
<label>使用 .trim:</label>
<input v-model.trim="trimmed" placeholder="输入带空格的文本" />
<p>值:"" (长度:)</p>
</div>
<div class="validation">
<h4>验证示例:</h4>
<p>未修剪:</p>
<p>已修剪:</p>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const untrimmed = ref("");
const trimmed = ref("");
</script>
<style scoped>
.trim-modifier {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
}
.input-group {
margin-bottom: 24px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.input-group input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
}
.input-group p {
margin-top: 8px;
color: #666;
font-size: 14px;
}
.validation {
padding: 16px;
background: #f5f5f5;
border-radius: 6px;
}
.validation h4 {
margin-top: 0;
color: #666;
}
.validation p {
margin: 8px 0;
}
</style>2. 自定义修饰符的实现
2.1 基础自定义修饰符
<!-- CustomModifiers.vue - 自定义修饰符基础 -->
<template>
<div class="custom-modifiers">
<h3>自定义修饰符示例</h3>
<div class="input-group">
<label>大写修饰符 (v-model.uppercase):</label>
<input
:value="uppercaseValue"
@input="handleUppercaseInput"
placeholder="输入文本自动转大写"
/>
<p>值:</p>
</div>
<div class="input-group">
<label>小写修饰符 (v-model.lowercase):</label>
<input
:value="lowercaseValue"
@input="handleLowercaseInput"
placeholder="输入文本自动转小写"
/>
<p>值:</p>
</div>
<div class="input-group">
<label>去空格修饰符 (v-model.trim):</label>
<input
:value="customTrimValue"
@input="handleCustomTrimInput"
placeholder="输入文本自动去空格"
/>
<p>值:""</p>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const uppercaseValue = ref("");
const lowercaseValue = ref("");
const customTrimValue = ref("");
const handleUppercaseInput = (event) => {
uppercaseValue.value = event.target.value.toUpperCase();
};
const handleLowercaseInput = (event) => {
lowercaseValue.value = event.target.value.toLowerCase();
};
const handleCustomTrimInput = (event) => {
customTrimValue.value = event.target.value.trim();
};
</script>
<style scoped>
.custom-modifiers {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
}
.input-group {
margin-bottom: 24px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.input-group input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
}
.input-group p {
margin-top: 8px;
color: #666;
font-size: 14px;
}
</style>2.2 组件中处理修饰符
<!-- ModifierInput.vue - 支持修饰符的输入组件 -->
<template>
<div class="modifier-input">
<input
:value="modelValue"
@input="handleInput"
:placeholder="placeholder"
class="input-field"
/>
</div>
</template>
<script setup>
const props = defineProps({
modelValue: [String, Number],
placeholder: String,
modifiers: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["update:modelValue"]);
const handleInput = (event) => {
let value = event.target.value;
// 处理修饰符
if (props.modifiers.uppercase) {
value = value.toUpperCase();
}
if (props.modifiers.lowercase) {
value = value.toLowerCase();
}
if (props.modifiers.trim) {
value = value.trim();
}
if (props.modifiers.number) {
value = parseFloat(value) || 0;
}
if (props.modifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1);
}
emit("update:modelValue", value);
};
</script>
<style scoped>
.modifier-input {
margin-bottom: 16px;
}
.input-field {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 14px;
}
.input-field:focus {
outline: none;
border-color: #42b883;
}
</style>2.3 使用自定义修饰符
<!-- UsingModifiers.vue - 使用自定义修饰符 -->
<template>
<div class="using-modifiers">
<h3>使用修饰符</h3>
<ModifierInput
v-model="value1"
:modifiers="{ uppercase: true }"
placeholder="大写输入"
/>
<p>值:</p>
<ModifierInput
v-model="value2"
:modifiers="{ lowercase: true }"
placeholder="小写输入"
/>
<p>值:</p>
<ModifierInput
v-model="value3"
:modifiers="{ trim: true }"
placeholder="去空格输入"
/>
<p>值:""</p>
<ModifierInput
v-model="value4"
:modifiers="{ number: true }"
placeholder="数字输入"
/>
<p>值: (类型:)</p>
<ModifierInput
v-model="value5"
:modifiers="{ capitalize: true }"
placeholder="首字母大写输入"
/>
<p>值:</p>
</div>
</template>
<script setup>
import { ref } from "vue";
import ModifierInput from "./ModifierInput.vue";
const value1 = ref("");
const value2 = ref("");
const value3 = ref("");
const value4 = ref("");
const value5 = ref("");
</script>
<style scoped>
.using-modifiers {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
}
p {
margin: 8px 0;
color: #666;
font-size: 14px;
}
</style>3. 修饰符在子组件中的处理
3.1 接收修饰符
<!-- ChildWithModifiers.vue - 子组件接收修饰符 -->
<template>
<div class="child-with-modifiers">
<input
:value="modelValue"
@input="handleInput"
:placeholder="placeholder"
class="input-field"
/>
<div class="modifier-info">
<h4>当前修饰符:</h4>
<ul>
<li v-if="modifiers?.lazy">.lazy</li>
<li v-if="modifiers?.trim">.trim</li>
<li v-if="modifiers?.number">.number</li>
<li v-if="modifiers?.uppercase">.uppercase</li>
<li v-if="!hasModifiers">无修饰符</li>
</ul>
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
modelValue: [String, Number],
placeholder: String,
modifiers: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["update:modelValue"]);
const hasModifiers = computed(() => {
return Object.keys(props.modifiers).length > 0;
});
const handleInput = (event) => {
let value = event.target.value;
// 应用修饰符
if (props.modifiers?.trim) {
value = value.trim();
}
if (props.modifiers?.uppercase) {
value = value.toUpperCase();
}
if (props.modifiers?.lowercase) {
value = value.toLowerCase();
}
if (props.modifiers?.number) {
value = parseFloat(value) || 0;
}
emit("update:modelValue", value);
};
</script>
<style scoped>
.child-with-modifiers {
padding: 24px;
background: #fff;
border-radius: 8px;
}
.input-field {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 14px;
margin-bottom: 16px;
}
.input-field:focus {
outline: none;
border-color: #42b883;
}
.modifier-info {
padding: 16px;
background: #f5f5f5;
border-radius: 6px;
}
.modifier-info h4 {
margin-top: 0;
color: #666;
}
.modifier-info ul {
margin: 0;
padding-left: 20px;
}
.modifier-info li {
margin: 8px 0;
color: #333;
}
</style>3.2 修饰符组合使用
<!-- CombinedModifiers.vue - 修饰符组合 -->
<template>
<div class="combined-modifiers">
<h3>修饰符组合示例</h3>
<div class="input-group">
<label>trim + uppercase:</label>
<ChildWithModifiers
v-model="value1"
:modifiers="{ trim: true, uppercase: true }"
placeholder="去空格并转大写"
/>
<p>值:</p>
</div>
<div class="input-group">
<label>number + trim:</label>
<ChildWithModifiers
v-model="value2"
:modifiers="{ trim: true, number: true }"
placeholder="去空格并转数字"
/>
<p>值: (类型:)</p>
</div>
<div class="input-group">
<label>capitalize + trim:</label>
<ChildWithModifiers
v-model="value3"
:modifiers="{ capitalize: true, trim: true }"
placeholder="首字母大写并去空格"
/>
<p>值:</p>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import ChildWithModifiers from "./ChildWithModifiers.vue";
const value1 = ref("");
const value2 = ref("");
const value3 = ref("");
</script>
<style scoped>
.combined-modifiers {
padding: 24px;
background: #fff;
border-radius: 8px;
}
h3 {
margin-top: 0;
margin-bottom: 24px;
}
.input-group {
margin-bottom: 24px;
}
.input-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.input-group p {
margin-top: 8px;
color: #666;
font-size: 14px;
}
</style>4. 实战示例:智能表单验证
<!-- SmartFormValidation.vue - 智能表单验证 -->
<template>
<div class="smart-form-validation">
<h3>智能表单验证(带修饰符)</h3>
<div class="form-field">
<label>用户名(trim + lowercase):</label>
<ChildWithModifiers
v-model="username"
:modifiers="{ trim: true, lowercase: true }"
placeholder="请输入用户名"
/>
<span class="error" v-if="errors.username">{{ errors.username }}</span>
</div>
<div class="form-field">
<label>邮箱(trim + 邮箱验证):</label>
<ChildWithModifiers
v-model="email"
:modifiers="{ trim: true }"
placeholder="请输入邮箱"
/>
<span class="error" v-if="errors.email">{{ errors.email }}</span>
</div>
<div class="form-field">
<label>年龄(number + 范围验证):</label>
<ChildWithModifiers
v-model="age"
:modifiers="{ trim: true, number: true }"
placeholder="请输入年龄"
/>
<span class="error" v-if="errors.age">{{ errors.age }}</span>
</div>
<div class="form-field">
<label>密码(trim + 强度验证):</label>
<ChildWithModifiers
v-model="password"
:modifiers="{ trim: true }"
placeholder="请输入密码"
/>
<span class="error" v-if="errors.password">{{ errors.password }}</span>
<div class="strength" v-if="password">
密码强度:{{ passwordStrength }}
</div>
</div>
<button @click="validateForm" class="validate-btn">验证表单</button>
<div class="form-data" v-if="isValid">
<h4>验证通过:</h4>
<pre>{{ formData }}</pre>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from "vue";
import ChildWithModifiers from "./ChildWithModifiers.vue";
const username = ref("");
const email = ref("");
const age = ref("");
const password = ref("");
const errors = reactive({
username: "",
email: "",
age: "",
password: "",
});
const isValid = ref(false);
const formData = computed(() => ({
username: username.value,
email: email.value,
age: age.value,
password: password.value,
}));
const passwordStrength = computed(() => {
const pwd = password.value;
if (!pwd) return "无";
let strength = 0;
if (pwd.length >= 8) strength++;
if (/[a-z]/.test(pwd)) strength++;
if (/[A-Z]/.test(pwd)) strength++;
if (/[0-9]/.test(pwd)) strength++;
if (/[^a-zA-Z0-9]/.test(pwd)) strength++;
const levels = ["弱", "较弱", "一般", "较强", "强"];
return levels[Math.min(strength, 4)];
});
const validateForm = () => {
let valid = true;
// 验证用户名
if (!username.value || username.value.length < 3) {
errors.username = "用户名至少 3 个字符";
valid = false;
} else {
errors.username = "";
}
// 验证邮箱
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email.value || !emailRegex.test(email.value)) {
errors.email = "请输入有效的邮箱地址";
valid = false;
} else {
errors.email = "";
}
// 验证年龄
if (!age.value || age.value < 1 || age.value > 150) {
errors.age = "年龄必须在 1-150 之间";
valid = false;
} else {
errors.age = "";
}
// 验证密码
if (!password.value || password.value.length < 6) {
errors.password = "密码至少 6 个字符";
valid = false;
} else {
errors.password = "";
}
isValid.value = valid;
};
</script>
<style scoped>
.smart-form-validation {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h3 {
margin-top: 0;
margin-bottom: 24px;
color: #333;
}
.form-field {
margin-bottom: 20px;
}
.form-field label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.error {
display: block;
margin-top: 4px;
color: #f44336;
font-size: 12px;
}
.strength {
display: block;
margin-top: 4px;
font-size: 12px;
color: #666;
}
.validate-btn {
width: 100%;
padding: 12px;
background: #42b883;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.validate-btn:hover {
background: #369970;
}
.form-data {
margin-top: 24px;
padding: 16px;
background: #f5f5f5;
border-radius: 6px;
}
.form-data h4 {
margin-top: 0;
color: #666;
}
.form-data pre {
background: #282c34;
color: #abb2bf;
padding: 16px;
border-radius: 6px;
}
</style>5. 课后 Quiz
题目 1:内置修饰符
问题: Vue 3 提供了哪些内置的 v-model 修饰符?
答案:
.lazy:失焦或按回车时同步.number:自动转为数字类型.trim:自动去除首尾空格
题目 2:自定义修饰符
问题: 如何在子组件中实现自定义修饰符?
答案:
通过 modifiers prop 接收修饰符对象,在事件处理函数中根据修饰符应用相应的逻辑。
题目 3:修饰符组合
问题: 多个修饰符可以同时使用吗?顺序有影响吗?
答案: 可以同时使用多个修饰符。修饰符按照声明的顺序依次应用。
6. 常见报错解决方案
报错 1:修饰符不生效
产生原因:
子组件未正确处理 modifiers prop
解决办法:
<!-- ✅ 在子组件中接收并处理 modifiers -->
<script setup>
const props = defineProps({
modifiers: Object,
});
const handleInput = (event) => {
let value = event.target.value;
if (props.modifiers?.trim) {
value = value.trim();
}
emit("update:modelValue", value);
};
</script>报错 2:number 修饰符无效
产生原因:
输入框类型不是 number
输入值无法转换为数字
解决办法:
<!-- ✅ 确保 type="number" 或手动转换 -->
<input v-model.number="value" type="number" />参考链接:https://vuejs.org/guide/components/v-model.html
余下文章内容请点击跳转至 个人博客页面 或者 扫描 关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:
往期文章归档
</details>
免费好用的热门在线工具
评论
发表评论