Vue3中的v-model、computed、watch

目录

一、v-model

二、v-bind

三、computed(计算属性)​

原理深度解析​​

​​经典使用场景​​

四、watch(侦听器)​​

​​核心作用​:​

​​原理深度解析​​

​​核心配置选项​​

​​经典使用场景​​

五、watchEffect(即时副作用)​​

​​核心作用​:​

​​原理深度解析​​

​​独特优势​​

​​经典使用场景​​


一、v-model

核心作用:​v-model 的主要功能是在​​表单输入元素​ ​或​​自定义 Vue 组件​ ​上创建​​双向数据绑定​​。它同时负责数据的读取(将 Vue 数据绑定到视图)和数据的写入(将用户输入同步回 Vue 数据)。

在原生表单元素上的实现原理:

html 复制代码
<!-- HTML 模板 -->
<input v-model="username">

等效于以下写法:

html 复制代码
<input 
  :value="username"
  @input="username = $event.target.value"
>

在自定义组件上的实现原理:

html 复制代码
<custom-input v-model="username">

等效于以下写法:

html 复制代码
<custom-input
  :model-value="username"
  @update:model-value="username = $event"
>

组件内部实现:

javascript 复制代码
// 自定义组件内部
props: ['modelValue'],
emits: ['update:modelValue'],

methods: {
  handleInput(e) {
    this.$emit('update:modelValue', e.target.value)
  }
}

二、v-bind

​核心作用:​v-bind 的核心功能是​​动态地将 Vue 实例(组件)中的数据绑定到 HTML 元素的属性(Attribute)或组件的属性(Prop)上​​。

:model ​​是 Vue 的动态属性绑定语法​​,由两部分组成:

  • 冒号' : '它是' v-bind:'的缩写
  • 属性名 model:任意自定义的属性名
  • 完整形式:v-bind:model="dataObj"

:model 不是一个内置指令或特殊属性,而是一种常见的命名约定,表示"传递整个模型对象"。

:model 的本质

  • v-bind:model 的简写形式
  • 仅用于单向数据传递(父 → 子)
  • 无内置行为,是纯属性绑定
  • 依赖开发者明确定义props接收

v-model 和 :model 的区别

​特性​ ​v-model​ ​:model​
​类型​ Vue指令 动态属性绑定
​功能​ 双向数据绑定 单向数据绑定
​实现​ 属性+事件绑定 仅属性绑定
​用途​ 表单输入组件 传递任意对象
​示例​ <input v-model="val"> <child :model="dataObj">
​组件实现​ 需要emit更新事件 仅接收props

适用场景选择

场景 推荐
​表单输入绑定​ v-model
​复杂数据传递​ :model
​组件通信​ 组合使用 :model + 自定义事件
​多个数据绑定​ 多个 v-model(如 v-model:user

三、computed(计算属性)​

**核心作用​:**​

创建​​响应式的派生数据​​,基于其他响应式数据自动计算结果并缓存

javascript 复制代码
import { computed } from 'vue'

// 基本用法
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

// 可写计算属性
const writableComputed = computed({
  get() { return modelValue.value },
  set(newVal) { modelValue.value = newVal }
})

let visibleDialog = computed({
  get() {
    return props.visible;
  },
  set() {
    closeDialog();
  },
});
原理深度解析​
  1. ​惰性求值​:仅当依赖项变化时才重新计算(首次访问时也会计算)
  2. ​依赖追踪​:运行时自动追踪getter函数内访问的响应式数据
  3. ​缓存机制​:依赖项未变化时直接返回缓存结果(多次访问不重复计算)
  4. ​响应式传播​:计算属性本身就是响应式ref,改变会自动触发依赖更新
  5. ​更新时机​:在Vue的响应式更新周期中同步执行
​经典使用场景​
  • 格式化/组合数据(如 ${firstName} ${lastName}
  • 过滤/转换数据(如 todos.filter(t => !t.done)
  • 复杂数学运算(如购物车总价计算)
  • 从props派生组件内部状态(需可写计算属性)
  • 替代模板中复杂表达式(提升可读性)

computed 的自动依赖追踪原理:

​computed 会监听内部所有依赖的响应式变量值变化,自动触发重新计算并缓存结果。​

javascript 复制代码
const tipsVisible1 = computed(() => repulseForm.backToModifyType == 1);

详细过程

​依赖收集阶段​​:

  • 当首次访问 computed 属性时,Vue 会执行计算函数
  • 计算函数内部访问的每一个响应式变量(如 repulseForm.backToModifyType) 都会通过"发布-订阅"机制将这个计算函数标记为自身的一个依赖

​响应更新阶段​​:

  • 当这些被依赖的响应式变量值改变时:
    • 所有依赖它的计算函数会自动触发重新计算
    • 结果会被缓存,如果依赖没有变化则直接返回缓存值

​缓存机制​​:

  • 当多次访问 chooseNodeVisible1 时:
  • 只有当 repulseForm.backToModifyType 改变时才会重新计算
  • 否则直接返回上次计算结果

示例:

javascript 复制代码
let visibleDialog = computed({
  get() {
    return props.visible;
  },
  set() {
    closeDialog();
  },
});

​触发 getter​​:

  • 首次访问时
  • 依赖项 (props.visible) 变化后再次访问时
  • 每次在模板中渲染时

​触发 setter​​:

  • 任何赋值操作 (visibleDialog.value = ...)
  • 即使赋予的值与当前值相同
  • 通过 v-model 绑定时被调用

四、watch(侦听器)​

​**​核心作用​:**​

​显式侦听特定数据源​ ​,在变化时执行副作用(异步请求、DOM操作等)

javascript 复制代码
import { watch } from 'vue'

// 监听单一源
watch(count, (newVal, oldVal) => {
  console.log(`计数变化: ${oldVal} → ${newVal}`)
})

// 监听多个源
watch([firstName, lastName], ([newFirst, newLast]) => {
  // 处理变化...
})

// 深度监听对象
watch(
  () => ({ ...user }), 
  (newUser) => { /* 处理 */ },
  { deep: true, immediate: true }
)
​原理深度解析​
  1. ​精确依赖​:必须显式指定监听的数据源(ref、reactive、getter函数)
  2. ​变化检测​ :默认浅层比较(对象需手动开启deep:true
  3. ​回调机制​:提供新旧值对比能力
  4. ​异步执行​ :默认在组件​更新后​ 执行(通过flush选项可调整时机)
  5. ​资源回收​:自动绑定组件生命周期(卸载时自动取消监听)
​核心配置选项​
选项 作用 典型场景
deep 深度监听嵌套对象变化 表单对象监听
immediate 立即执行回调 初始数据加载
flush 控制回调执行时机 DOM操作时使用'post'
​经典使用场景​
  • 异步操作、副作用操作
  • API数据获取(搜索词变化时发起请求)
  • 路由参数监听($route.params.id变化时加载数据)
  • 表单验证(字段变化时触发验证)
  • 操作历史记录(状态变化时记录操作)

深度监听:

javascript 复制代码
watch(
  // 源获取函数:只监听user.profile对象
  () => user.profile,
  
  // 变化回调
  (newProfile) => {
    console.log("用户资料更新:", newProfile);
    // 执行相关操作
  },
  
  // 配置选项
  { deep: true } // 启用深度监听
);
javascript 复制代码
// 以下操作都会触发监听:
user.profile.name = "张三";     // 修改属性
user.profile.address.city = "北京"; // 修改嵌套属性
user.profile.hobbies.push("阅读");  // 修改数组内容
javascript 复制代码
// 如果需要新旧值对比,应创建副本:
watch(
  () => ({ ...user.profile }), // 创建浅拷贝
  (newProfile, oldProfile) => {
    // 现在可以比较具体变化
  },
  { deep: true }
);
javascript 复制代码
// watchEffect精确控制
watchEffect(() => {
  // 只追踪需要的具体属性
  if (needTracking) {
    const importantValue = someObj.deep.nested.value;
    // 使用importantValue...
  }
});

五、watchEffect(即时副作用)​

​**​核心作用​:**​

​自动追踪依赖​ ​并立即执行副作用,响应式数据变化时自动重新运行

javascript 复制代码
import { watchEffect } from 'vue'

// 基本用法
const stop = watchEffect(() => {
  document.title = `消息(${unreadCount.value})`
})

// 清理副作用
watchEffect((onCleanup) => {
  const timer = setTimeout(() => {...}, 1000)
  onCleanup(() => clearTimeout(timer))
})

// 控制执行时机
watchEffect(() => {...}, { flush: 'post' })
​原理深度解析​
  1. ​自动依赖收集​:运行时自动追踪函数内访问的所有响应式数据
  2. ​立即执行​ :首次调用时同步执行(与watch的immediate模式不同)
  3. ​智能清理​ :通过onCleanup注册清理函数(下次执行前调用)
  4. ​动态依赖​:每次执行重新收集依赖(条件分支变化时自动调整)
  5. ​执行控制​ :默认在组件​更新前​ 执行(DOM操作用flush: 'post'
​独特优势​
  • 避免手动维护依赖列表
  • 处理动态依赖(条件分支中的响应式数据)
  • 更符合命令式编程思维
  • 简化初始化逻辑(自动执行)
​经典使用场景​
  • 实时更新DOM(如标题、滚动位置)
  • 自动请求取消(onCleanup中取消请求)
  • 监听鼠标/键盘事件(自动取消注册)
  • 表单自动保存(输入变化后定时保存)
  • 日志记录(自动跟踪变化数据)

​三者的对比决策表​

特性 computed watch watchEffect
​返回类型​ 响应式ref
​执行时机​ 需要时计算 变化后回调 立即+变化时
​依赖声明​ 自动 显式声明 自动
​新旧值​ 提供
​缓存机制​
​异步支持​ ❌(纯函数)
​清理机制​ ✅(onCleanup)
​典型用途​ 派生数据 响应变化 执行副作用

**如何选择?**​

  1. 需要​推导新数据​ 吗? → computed
  2. 需要​获取变化前后值​ 吗? → watch
  3. 操作​包含异步/副作用​ 吗?
    • 依赖明确 → watch
    • 依赖动态 → watchEffect
  4. 需要​自动执行初始化​ ? → watchEffect
  5. 涉及​资源清理​ ? → watchEffect(onCleanup)

​最佳实践​ ​:优先使用computed派生数据,用watchEffect处理副作用,只在需要精确控制时使用watch

总结:

computed

  • 自动追踪依赖关系
  • 懒计算(在访问时执行)
  • 结果缓存(依赖未变则返回缓存)
  • 适用于模板绑定、值派生、多依赖计算

watch

  • 显示声明监听目标
  • 即时响应数据变化
  • 支持异步操作
  • 适用于数据联动、异步操作、复杂副作用处理

相关推荐
七月的冰红茶2 分钟前
【threejs】第一人称视角之八叉树碰撞检测
前端·threejs
爱掉发的小李18 分钟前
前端开发中的输出问题
开发语言·前端·javascript
祝余呀1 小时前
HTML初学者第四天
前端·html
浮桥2 小时前
vue3实现pdf文件预览 - vue-pdf-embed
前端·vue.js·pdf
七夜zippoe2 小时前
前端开发中的难题及解决方案
前端·问题
Hockor3 小时前
用 Kimi K2 写前端是一种什么体验?还支持 Claude Code 接入?
前端
杨进军3 小时前
React 实现 useMemo
前端·react.js·前端框架
海底火旺4 小时前
浏览器渲染全过程解析
前端·javascript·浏览器
你听得到114 小时前
揭秘Flutter图片编辑器核心技术:从状态驱动架构到高保真图像处理
android·前端·flutter
驴肉板烧凤梨牛肉堡4 小时前
浏览器是否支持webp图像的判断
前端