公司动态

计算属性与侦听器

📅 2026/8/11 3:36:20
计算属性与侦听器
计算属性与侦听器computed从现有数据“算”出新数据有缓存无副作用watch监听数据变化执行副作用如请求、日志、操作上报1. 计算属性 computed为什么需要computed模板中直接写复杂表达式会导致臃肿、重复、不可复用。把“根据现有数据算出新数据”的逻辑放在script setup中模板只负责渲染。基本用法import{ref,computed}fromvueconstkeywordref()constusersref([...])// 源数据constfilteredUserscomputed((){returnusers.value.filter(useruser.name.includes(keyword.value))})三个要点模板中直接使用不要加.value自动解包v-foruser in filteredUsers脚本中必须用.value读取值filteredUsers.value.length只读不能给计算属性赋值要改变结果就修改它依赖的源数据缓存特性computed 会缓存计算结果只有当依赖的响应式数据变化时才重新计算多次使用同一个计算属性只会执行一次计算后续读缓存对比普通方法方法每次被调用都会重新执行无论依赖是否变化模板渲染时多次调用方法就会执行多次无缓存类比computed 像电子价目牌商品不变就不动方法像收银员每接一个任务就重新计算误区计算函数里如果使用了非响应式数据如Date.now()Vue 无法感知变化缓存不会失效。典型应用搜索 筛选同时根据关键字和状态过滤列表两个条件用 AND 叠加constkeywordref()conststatusFilterref(全部)constfilteredUserscomputed((){returnusers.value.filter(user{constmatchKeywordkeyword.value||user.name.includes(keyword.value)||user.dept.includes(keyword.value)constmatchStatusstatusFilter.value全部||user.statusstatusFilter.valuereturnmatchKeywordmatchStatus})})模板中直接遍历filteredUsers人数统计直接用filteredUsers.length。2. 侦听器 watch与 computed 的区别computed 是纯计算不应包含副作用请求、日志等watch 专门用于数据变化时执行副作用口诀能算就用 computed要动才用 watch基本用法import{ref,watch}fromvueconstkeywordref()watch(keyword,(newVal,oldVal){console.log(关键词从「${oldVal}」变为「${newVal}」)// 可以在这里发请求、记日志、保存数据等})回调参数(新值, 旧值)监听多个数据源watch([keyword,statusFilter],([newKeyword,newStatus],[oldKeyword,oldStatus]){// 任一数据变化都触发})防抖处理debounce连续触发时只执行最后一次如输入暂停 500ms 后再发起请求lettimernullwatch(keyword,(newVal){clearTimeout(timer)timersetTimeout((){// 执行上报或请求},500)})清除上一次的定时器保证只有停止输入超过指定时间才会执行。选项immediate立即执行默认 watch 只在数据第一次变化后才触发设置immediate: true可在页面初始化时立即执行一次回调此时oldValue为undefinedwatch(statusFilter,(newVal){// 初始化时也会执行},{immediate:true})常用于进入页面立即加载数据、记录初始状态等。选项deep深度监听普通 watch 只监听引用类型变量的引用变化如数组整体替换如果需要监听对象内部属性或数组元素变化设置deep: truewatch(users,(){// 当 users 数组内某个对象的属性被修改时触发},{deep:true})注意事项deep有性能损耗大对象或深层嵌套对象要小心使用深度监听时回调中的新旧值是同一个引用无法获得变化前的快照newVal oldVal如果需要获取旧值自己在外层保存快照监听 reactive 对象的某个属性getter 形式import{reactive,watch}fromvueconststatereactive({name:张三,age:30})watch(()state.name,(newVal,oldVal){// 只监听 name 属性无需 deep})这种写法更精确、性能更好。3. computed vs watch 选型总结computedwatch用途派生数据计算新值执行副作用动作返回值有返回值可直接在模板使用无返回值只执行回调缓存有依赖不变不重复计算无每次变化都触发触发条件依赖变化时自动重新计算被监听数据变化时执行回调典型场景搜索过滤、列表统计、格式化显示数据变化请求接口、日志上报、防抖搜索、配置保存依赖多个数据多个响应式数据都可作为依赖可监听单个或多个数据源异步支持不能包含副作用适合放异步请求选择原则需要得到一个新数据→ computed需要在数据变化时做一件事→ watch两者常配合使用并非二选一。4. 关键代码速查搜索筛选computedconstfilteredUserscomputed((){returnusers.value.filter(user{constmatchKeywordkeyword.value||user.name.includes(keyword.value)||user.dept.includes(keyword.value)constmatchStatusstatusFilter.value全部||user.statusstatusFilter.valuereturnmatchKeywordmatchStatus})})联动上报watch 防抖lettimernullwatch([keyword,statusFilter],([newKeyword,newStatus]){clearTimeout(timer)timersetTimeout((){addLog(筛选条件已变化关键字「${newKeyword}」、状态「${newStatus}」)},500)})immediate deep 示例// 页面打开立即记录当前筛选状态watch(statusFilter,(newStatus){addLog(当前筛选状态为「${newStatus}」)},{immediate:true})// 监听用户列表内部变化watch(users,(){addLog(用户列表数据发生变化)},{deep:true})5. 常见错误排查现象原因解决模板中filteredUsers.value报错模板自动解包不需要.value直接写filteredUsers搜索无效列表不变v-for仍遍历源数据users改为filteredUsers给计算属性赋值报错computed 只读修改依赖的源数据点击“切换状态”日志不触发没有加deep: true监听对象/数组内部变化需加deep每输入一个字符都上报缺少防抖用clearTimeout/setTimeout控制刷新页面没有初始化日志忘记immediate: true在 watch 配置中添加deep 监听时新、旧值相同深度监听的新旧值是同引用需要旧值请自行深拷贝保存