Vue 3 组件 v-model 完全指南(五):修饰符使用完全指南——内置修饰符与自定义修饰符详解

扫描 二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长

发现 1000+ 提升效率与开发的 AI 工具和实用程序https://tools.cmdragon.cn/

1. 内置修饰符详解

1.1 .lazy 修饰符

<!-- LazyModifier.vue - .lazy 修饰符 -->
<template>
 <div class="lazy-modifier">
   <h3>.lazy 修饰符示例</h3>

   <div class="input-group">
     <label>默认(实时同步):</label>
     <input v-model="defaultValue" placeholder="实时同步" />
     <p>值:{{ defaultValue }}</p>
   </div>

   <div class="input-group">
     <label>使用 .lazy(失焦同步):</label>
     <input v-model.lazy="lazyValue" placeholder="失焦时同步" />
     <p>值:{{ lazyValue }}</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>值:{{ stringValue }} (类型:{{ typeof stringValue }})</p>
   </div>

   <div class="input-group">
     <label>使用 .number:</label>
     <input
       v-model.number="numberValue"
       type="number"
       placeholder="数字类型"
     />
     <p>值:{{ numberValue }} (类型:{{ typeof numberValue }})</p>
   </div>

   <div class="calculation">
     <h4>计算示例:</h4>
     <p>字符串相加:{{ stringValue }} + 1 = {{ stringValue }}1</p>
     <p>数字相加:{{ numberValue }} + 1 = {{ numberValue + 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>值:"{{ untrimmed }}" (长度:{{ untrimmed?.length || 0 }})</p>
   </div>

   <div class="input-group">
     <label>使用 .trim:</label>
     <input v-model.trim="trimmed" placeholder="输入带空格的文本" />
     <p>值:"{{ trimmed }}" (长度:{{ trimmed?.length || 0 }})</p>
   </div>

   <div class="validation">
     <h4>验证示例:</h4>
     <p>未修剪:{{ untrimmed && untrimmed.trim() ? "有效" : "无效" }}</p>
     <p>已修剪:{{ trimmed ? "有效" : "无效" }}</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>值:{{ uppercaseValue }}</p>
   </div>

   <div class="input-group">
     <label>小写修饰符 (v-model.lowercase):</label>
     <input
       :value="lowercaseValue"
       @input="handleLowercaseInput"
       placeholder="输入文本自动转小写"
     />
     <p>值:{{ lowercaseValue }}</p>
   </div>

   <div class="input-group">
     <label>去空格修饰符 (v-model.trim):</label>
     <input
       :value="customTrimValue"
       @input="handleCustomTrimInput"
       placeholder="输入文本自动去空格"
     />
     <p>值:"{{ customTrimValue }}"</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>值:{{ value1 }}</p>

   <ModifierInput
     v-model="value2"
     :modifiers="{ lowercase: true }"
     placeholder="小写输入"
   />
   <p>值:{{ value2 }}</p>

   <ModifierInput
     v-model="value3"
     :modifiers="{ trim: true }"
     placeholder="去空格输入"
   />
   <p>值:"{{ value3 }}"</p>

   <ModifierInput
     v-model="value4"
     :modifiers="{ number: true }"
     placeholder="数字输入"
   />
   <p>值:{{ value4 }} (类型:{{ typeof value4 }})</p>

   <ModifierInput
     v-model="value5"
     :modifiers="{ capitalize: true }"
     placeholder="首字母大写输入"
   />
   <p>值:{{ value5 }}</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>值:{{ value1 }}</p>
   </div>

   <div class="input-group">
     <label>number + trim:</label>
     <ChildWithModifiers
       v-model="value2"
       :modifiers="{ trim: true, number: true }"
       placeholder="去空格并转数字"
     />
     <p>值:{{ value2 }} (类型:{{ typeof value2 }})</p>
   </div>

   <div class="input-group">
     <label>capitalize + trim:</label>
     <ChildWithModifiers
       v-model="value3"
       :modifiers="{ capitalize: true, trim: true }"
       placeholder="首字母大写并去空格"
     />
     <p>值:{{ value3 }}</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

余下文章内容请点击跳转至 个人博客页面 或者 扫描 二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:Vue 3 组件 v-model 第五章:修饰符使用完全指南

往期文章归档

</details>

免费好用的热门在线工具

</details>

评论

此博客中的热门博文

深入探讨聚合函数(COUNT, SUM, AVG, MAX, MIN):分析和总结数据的新视野

数据库与编程语言的连接

数据库的创建与删除:理论与实践