gogogo
管理员
管理员
  • UID25
  • 粉丝0
  • 关注0
  • 发帖数1442
阅读:59回复:1

Vue3中 watch、watchEffect 详解

楼主#
更多 发布于:2025-06-11 15:21


1. watch 的使用

语法

import { watch } from "vue"
watch( name , ( curVal , preVal )=>{
               //业务处理  
}, options ) ;

共有三个参数,分别为:
name:需要帧听的属性;
(curVal,preVal)=>{ //业务处理 } 箭头函数,是监听到的最新值和本次修改之前的值,此处进行逻辑处理。
options :配置项,对监听器的配置,如:是否深度监听。


1.1 监听 ref 定义的响应式数据


<template>
  <div>
    <div>值:pw_count</div>
    <button @click="add">改变值</button>
  </div>
</template>
 
<script>
import { ref, watch } from 'vue';
export default {
  setup(){
    const count = ref(0);
    const add = () => {
      count.value ++
    }; watch(count,(newVal,oldVal) => {
      console.log('值改变了',newVal,oldVal)
    })
return {
      count,
      add,
    }
  }
}
</script>




1.2 监听 reactive 定义的响应式数据

<template>
  <div>
    <div>pw_obj.name</div>
    <div>pw_obj.age</div>
    <button @click="changeName">改变值</button>
  </div>
</template>
 
<script>
import { reactive, watch } from 'vue';
export default {
  setup(){
    const obj = reactive({
      name:'zs',
      age:14
    });
    const changeName = () => {
      obj.name = 'ls';
    }; watch(obj,(newVal,oldVal) => {
      console.log('值改变了',newVal,oldVal)
    })
return {
      obj,
      changeName,
    }
  }
}
</script>


1.3 监听多个响应式数据数据




<template>
  <div>
    <div>pw_obj.name</div>
    <div>pw_obj.age</div>
    <div>pw_count</div>
    <button @click="changeName">改变值</button>
  </div>
</template>
 
<script>
import { reactive, ref, watch } from 'vue';
export default {
  setup(){
    const count = ref(0);
    const obj = reactive({
      name:'zs',
      age:14
    });
    const changeName = () => {
      obj.name = 'ls';
    }; watch([count,obj],() => {
      console.log('监听的多个数据改变了')
    })
return {
      obj,
      count,
      changeName,
    }
  }
}
</script>
1.4 监听对象中某个属性的变化





<template>
  <div>
    <div>pw_obj.name</div>
    <div>pw_obj.age</div>
    <button @click="changeName">改变值</button>
  </div>
</template>
 
<script>
import { reactive, watch } from 'vue';
export default {
  setup(){
    const obj = reactive({
      name:'zs',
      age:14
    });
    const changeName = () => {
      obj.name = 'ls';
    }; watch(() => obj.name,() => {
      console.log('监听的obj.name改变了')
    })
return {
      obj,
      changeName,
    }
  }
}
</script>


1.5 深度监听(deep)、默认执行(immediate)


<template>
  <div>
    <div>pw_obj.brand.name</div>
    <button @click="changeBrandName">改变值</button>
  </div>
</template>
 
<script>
import { reactive, ref, watch } from 'vue';
export default {
  setup(){
    const obj = reactive({
      name:'zs',
      age:14,
      brand:{
        id:1,
        name:'宝马'
      }
    });
    const changeBrandName = () => {
      obj.brand.name = '奔驰';
    }; watch(() => obj.brand,() => {
            console.log('监听的obj.brand.name改变了')
        },{
           deep:true,
           immediate:true,
    })
return {
      obj,
      changeBrandName,
    }
  }
}
</script>
gogogo
管理员
管理员
  • UID25
  • 粉丝0
  • 关注0
  • 发帖数1442
沙发#
发布于:2025-06-11 15:26
2. watchEffect 的使用
watchEffect 也是一个帧听器,是一个副作用函数。
它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听。


<template>
  <div>
    <input type="text" v-model="obj.name">
  </div>
</template>
 
<script>
import { reactive, watchEffect } from 'vue';
export default {
  setup(){
    let obj = reactive({
      name:'zs'
    });


  watchEffect(() => {
      console.log('name:',obj.name)
    })

      return {
          obj
      }
  }
}
</script>


停止侦听
当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
在一些情况下,也可以显式调用返回值以停止侦听:


<template>
  <div>
    <input type="text" v-model="obj.name">
    <button @click="stopWatchEffect">停止监听</button>
  </div>
</template>
 
<script>
import { reactive, watchEffect } from 'vue';
export default {
  setup(){
    let obj = reactive({
      name:'zs'
    }); const stop = watchEffect(() => {
      console.log('name:',obj.name)
    })
    const stopWatchEffect = () => {
      console.log('停止监听')
      stop();
    }
return {
      obj,
      stopWatchEffect,
    }
  }
}
</script>
清除副作用
有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (场景:有一个页码组件里面有5个页码,点击就会异步请求数据。于是做一个监听,监听当前页码,只要有变化就请求一次。问题:如果点击的比较快,从1到5全点了一遍,那么会有5个请求,最终页面会显示第几页的内容?第5页?那是假定请求第5页的ajax响应的最晚,事实呢?并不一定。于是这就会导致错乱。还有一个问题,连续快速点5次页码,等于我并不想看前4页的内容,那么是不是前4次的请求都属于带宽浪费?这也不好。
于是官方就给出了一种解决办法:
侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。
当以下情况发生时,这个失效回调会被触发:

副作用即将重新执行时;
侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)


watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()
  })
})
首先,异步操作必须是能中止的异步操作,对于定时器来讲中止定时器很容易,clearInterval之类的就可以,但对于ajax来讲,需要借助ajax库(比如axios)提供的中止ajax办法来中止ajax。
现在我写一个能直接运行的范例演示一下中止异步操作:
先搭建一个最简[backcolor=url(&amp]Node服务器,3000端口的:





const http = require('http');
 
const server = http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', "*");
  res.setHeader('Access-Control-Allow-Credentials', true);
  res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
  res.writeHead(200, { 'Content-Type': 'application/json'});
});
 
server.listen(3000, () => {
  console.log('Server is running...');
});
 
server.on('request', (req, res) => {
  setTimeout(() => {
    if (/\d.json/.test(req.url)) {
      const data = {
        content: '我是返回的内容,来自' + req.url
      }
      res.end(JSON.stringify(data));
    }
  }, Math.random() * 3000);
});







<template>
  <div>
    <div>content: pw_ content </div>
    <button @click="changePageNumber">第pw_ pageNumber 页</button>
  </div>
</template>
 
<script>
import axios from 'axios';
import { ref, watchEffect } from 'vue';
export default {
  setup() {
    let pageNumber = ref(1);
    let content = ref('');
 
    const changePageNumber = () => {
      pageNumber.value++;
    } watchEffect((onInvalidate) => {
       axios.get(`http://localhost:3000/${pageNumber.value}.json`, {
          // cancelToken: source.token,
       }).then((response) => {
        content.value = response.data.content;
       }).catch(function (err) {
            if (axios.isCancel(err)) {
               console.log('Request canceled', err.message);
             }
      });
    });
    return {
      pageNumber,
      content,
      changePageNumber,
    };
  },
};
</script>
上面注释掉的代码先保持注释,然后经过多次疯狂点击之后,得到这个结果,显然,内容错乱了:

………………


https://blog.csdn.net/wish_way/article/details/134420294?ops_request_misc=%257B%2522request%255Fid%2522%253A%25226ca265d512761906b18599b29ec2c5b2%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=6ca265d512761906b18599b29ec2c5b2&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-134420294-null-null.142^v102^pc_search_result_base7&utm_term=vue3%20%20watchEffect&spm=1018.2226.3001.4187
游客


返回顶部