你可以把它们看成是同一个功能的两种不同形式,底层实现是一样的。watch-显式指定依赖源,当依赖源更新时执行回调函数。watchEffect-自动收集依赖源,并在依赖源更新时重新执行自己的响应式依赖收集。首先,您需要了解vue3的响应性是如何工作的。这部分我想很多文章都详细讨论过,这里简单提一下。这是ref的简化实现:constref=(initialValue)=>{let_value=initialValuereturn{getvalue(){track(this,'value')//收集依赖关系return_value},setvalue(){_value=valuetrigger(this,'value')//触发依赖}}}访问数据时会调用track,记录访问的字段。写数据的时候会调用trigger,触发这个字段绑定的事件的更新(也就是最底层的effect——computed,watch,watchEffect是effect),它们会记录在一个全局的WeakMap中,这里我就不展开了,有兴趣的可以去看源码。constcounter=ref(1)console.log(counter.value)//`track()`被调用counter.value=2//`trigger()`当我们在函数中需要依赖时被调用,只是记录的次数在函数执行期间调用track()的次数(以及相应的对象和字段)就足够了。例如:constcounter=ref(1)functionfoo(){console.log(counter.value)}functioncollectDeps(){startTracking()foo()//在这个过程中,收集计数器stopTracking()}所以你可以知道函数foo依赖于counter。Watch一个通用的watchapi应该是这样的(其他直接接受ref或reactive作为参数的实际上是sugar)。watch(()=>{/*依赖源集合函数*/},()=>{/*依赖源变化时的回调函数*/})这里的依赖源函数只会执行一次,回调函数会每次依赖源发生变化时触发,但不收集回调函数上的依赖。也就是说,依赖源和回调函数之间没有必然的直接关系。WatchEffectWatchEffect相当于结合了watch的依赖源和回调函数。当你需要的任何响应式依赖更新时,回调函数将被重新执行。与watch不同的是,watchEffect的回调函数会立即执行(即{immediate:true})。watchEffect(()=>{/*依赖源也是回调函数*/})下面两种用法在行为上基本等价:watchEffect(()=>console.log(counter.value))watch(()=>counter.value,()=>console.log(counter.value),{immediate:true})和watch不同的是,在watchEffect中,依赖source会被重复执行,新添加的依赖也会被动态收集,例如:constcounter=ref(0)constenabled=ref(false)watchEffect(()=>{if(enabled.value)console.log(counter.value)})//(下面忽略nextTick)//watchEffect会立即执行,因为"enabled"为false,此时只收集到"enabled"。依赖counter.value+=1//没有响应enabled.value=true//效果被触发,控制台显示“1”counter.value+=1//“counter”被收集为一个新的依赖,“2”"控制台显示enabled.value=false//函数重新执行,无输出counter.value+=1//函数重新执行,无输出Output(虽然计数器没用,但还是会作为依赖项触发函数)。顺便说一句,computed实际上类似于带输出的watchEffect的同步版本。什么时候用什么?建议大部分时候使用watch显式指定依赖,避免不必要的重复触发,也避免后续代码修改或重构时不小心引入新的依赖。watchEffect适用于一些逻辑比较简单,依赖源和逻辑强相关的场景(或者懒惰的场景)。
