Vue2 中 v-model 处理不同数据结构的技巧

1. 基本用法回顾

v-model:value + @input 的语法糖:

vue 复制代码
<!-- 等价于 -->
<input v-model="msg" />
<input :value="msg" @input="msg = $event.target.value" />

在组件上,v-model 默认绑定 value prop 和 input 事件。


2. 处理不同数据结构

2.1 基本类型(String / Number / Boolean)

vue 复制代码
<template>
  <input v-model="name" />
  <input v-model.number="age" type="number" />
  <input v-model.trim="name" />
  <input v-model.lazy="name" />
</template>

<script>
export default {
  data() {
    return {
      name: '',
      age: 0
    }
  }
}
</script>

修饰符

  • .number --- 自动转为数值
  • .trim --- 自动去除首尾空格
  • .lazy --- 在 change 事件而非 input 事件触发更新

2.2 对象(Object)

方式一:直接绑定整个对象(不推荐,难维护)

vue 复制代码
<user-form v-model="user"></user-form>

方式二:拆开绑定(推荐)

vue 复制代码
<!-- 父组件 -->
<user-form
  :name.sync="user.name"
  :email.sync="user.email"
/>

<!-- UserForm 组件内部 -->
<script>
export default {
  props: ['name', 'email'],
  methods: {
    updateName(val) {
      this.$emit('update:name', val)
    }
  }
}
</script>

方式三:自定义 input 事件携带整个对象

vue 复制代码
<!-- 子组件 -->
<script>
export default {
  props: ['value'],
  methods: {
    updateField(key, val) {
      const newObj = { ...this.value, [key]: val }
      this.$emit('input', newObj)
    }
  }
}
</script>

2.3 数组(Array)

v-model 不能直接绑定数组的「某一项」并自动触发更新,需要借助额外技巧。

问题:直接绑定数组项无效

vue 复制代码
<!-- ❌ 这样写不会触发视图更新 -->
<div v-for="(item, index) in list" :key="index">
  <input v-model="list[index]" />
</div>

解法一:用 this.$set(Vue2 响应式坑)

vue 复制代码
<script>
export default {
  data() {
    return {
      list: []
    }
  },
  mounted() {
    // ✅ 正确方式:确保响应式
    this.$set(this.list, 0, 'hello')
  }
}
</script>

解法二:用对象数组 + 绑定对象属性

vue 复制代码
<template>
  <div v-for="(item, index) in list" :key="index">
    <!-- ✅ 绑定对象的属性,Vue2 可检测 -->
    <input v-model="item.text" />
    <input type="checkbox" v-model="item.done" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { text: '学习 Vue', done: false },
        { text: '写笔记', done: true }
      ]
    }
  }
}
</script>

解法三:自定义组件封装数组项

vue 复制代码
<!-- ArrayItem.vue -->
<script>
export default {
  props: ['value'],
  render(h) {
    return h('input', {
      domProps: { value: this.value },
      on: {
        input: (e) => this.$emit('input', e.target.value)
      }
    })
  }
}
</script>

<!-- 父组件 -->
<array-item v-for="(item, i) in list" :key="i"
  :value="list[i]"
  @input="val => $set(list, i, val)"
/>

2.4 嵌套对象(Deep Nested Object)

深层嵌套对象使用 v-model 时,需确保每一层都是响应式的。

vue 复制代码
<script>
export default {
  data() {
    return {
      form: {
        user: {
          profile: {
            name: '',
            avatar: ''
          }
        }
      }
    }
  },
  methods: {
    // ✅ 使用 $set 确保嵌套属性响应式
    initForm() {
      this.$set(this.form.user, 'profile', {
        name: 'Weiming',
        avatar: ''
      })
    }
  }
}
</script>

<template>
  <div>
    <input v-model="form.user.profile.name" />
    <input v-model="form.user.profile.avatar" />
  </div>
</template>

注意:Vue2 的响应式系统无法检测:

  • 直接给对象新增属性(用 this.$set(obj, key, val)
  • 直接通过索引修改数组项(用 this.$set(arr, index, val)

3. Vue2 特有的 .sync 修饰符

Vue2 中,除了 v-model,还可以用 .sync 实现「多个 prop 双向绑定」:

vue 复制代码
<!-- 父组件 -->
<my-component
  :title.sync="pageTitle"
  :content.sync="pageContent"
/>

<!-- 子组件 -->
<script>
export default {
  props: ['title', 'content'],
  methods: {
    updateTitle(newTitle) {
      this.$emit('update:title', newTitle)
    }
  }
}
</script>

.sync 在 Vue3 中被移除,改用 v-model:title 语法。


4. 自定义组件中的 v-model(多数据类型)

4.1 自定义 prop 和事件名

vue 复制代码
<script>
export default {
  model: {
    prop: 'checked',   // 代替默认的 value
    event: 'change'    // 代替默认的 input
  },
  props: ['checked'],
  methods: {
    toggle() {
      this.$emit('change', !this.checked)
    }
  }
}
</script>

4.2 复选框组(数组类型)

vue 复制代码
<!-- CheckboxGroup.vue -->
<script>
export default {
  props: ['value'],
  methods: {
    toggle(option) {
      const newValue = this.value.includes(option)
        ? this.value.filter(v => v !== option)
        : [...this.value, option]
      this.$emit('input', newValue)
    }
  }
}
</script>

<!-- 使用 -->
<checkbox-group v-model="selectedFruits" />

5. 常见坑与解决方案

问题 原因 解决方案
数组项修改不触发视图更新 Vue2 无法检测索引赋值 this.$set(arr, index, val)
对象新增属性无响应 Vue2 无法检测新增属性 this.$set(obj, key, val)
v-model 绑定原始数组项 原始值无引用,无法响应式 改用对象数组
多个值需要双向绑定 v-model 只能绑定一个 使用 .sync 修饰符
深度嵌套对象修改无响应 嵌套层级过深 this.$setJSON.parse(JSON.stringify()) 强制刷新

6. 最佳实践总结

  1. 简单场景 :直接用 v-model
  2. 对象/表单场景 :拆成多个 .sync,或封装成自定义组件
  3. 数组场景:用对象数组,避免直接绑定原始值
  4. 深层嵌套:初始化时把结构写完整,避免动态新增属性
  5. Vue2 → Vue3 迁移 :提前用 v-model:propName 替代 .sync 的写法
相关推荐
李明卫杭州1 小时前
使用 computed 处理 v-model 复杂数据结构
前端·javascript·vue.js
丨我是张先生丨1 小时前
日语单词 Web Page
前端·css·css3
禅思院3 小时前
AI对话前端从入门到崩溃:一个长对话引发的五层优化战争【引子】
前端·面试·架构
TrisighT3 小时前
Electron 鸿蒙 PC 上点外链唤醒应用,我试了 6 种写法只有 1 种能跑
前端·electron·harmonyos
2501_930707784 小时前
如何将HTML文件转换为纯文本(详细步骤指南)
前端·html
天才熊猫君4 小时前
配置与数据分离:一种可视化搭建的属性编辑方案
前端·javascript
林希_Rachel_傻希希5 小时前
web性能之相关路径——AI总结
前端·javascript·面试
不好听6135 小时前
从零搭建一个 RAG 语义搜索系统 —— DEMO的初始阶段
javascript·面试·llm
何时梦醒5 小时前
上下文工程(Context Engineering):AI 应用开发的新范式 —— 从理论到实战全解析
javascript