>[success] # 计算属性 -- computed ~~~ 1.基本规则和'option Api' 计算属性规则类似,返回值是一个ref 对象 ~~~ >[danger] ##### 案例 ~~~ 1.下面案例返回是一个不变的响应式ref 对象,因此进行改写会报错 ~~~ ~~~ const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误 ~~~ >[danger] ##### 案例 ~~~ 1.和 'option Api' 一样可以定义getter 和setter,传入的是一个对象 ~~~ ~~~ const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0 ~~~ >[success] # watchEffect ~~~ 1.'watchEffect ' 是一个新的api,需要将这个单词拆解来看'watch' 和'effect' ,'effect'全称叫side effect,副作用。 关于副作用可以看这个'https://www.kancloud.cn/cyyspring/more/1979438#_31',或者思考一下你在watch里面 进行异步请求,这个请求是随时的不可控的这个就是一种副作用,所以可以理解成'watchEffect'的意思就是在观察 (watch)到变化后执行一些操作(effect) 2.哪能解决什么问题接口请求就是一个 side effect,假设我们现在用一个用户ID去查询用户的详情信息, 然后我们监听了这个用户ID, 当用户ID 改变的时候我们就会去发起一次请求,这很简单,用watch 就可以做到。 但是如果在请求数据的过程中,我们的用户ID发生了多次变化,那么我们就会发起多次请求,而最后一次返回的 数据将会覆盖掉我们之前返回的所有用户详情。这不仅会导致资源浪费,还无法保证 watch 回调执行的顺序。而 使用 watchEffect 我们就可以做到 3.使用起来他传入的是一个函数,这个函数可以响应式追踪依赖,具体来看'watchEffect' 能做什么 3.1.watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行 3.2.'watchEffect ' 和'watch' 不同就是可以监听到内部所有的属性变化这点和'computed'类似,既然要可以收集 内部变化,所以'watchEffect ' 会默认初始时就会执行第一次, 从而可以收集需要监视的数据 4.调用返回值以停止侦听 5.传入'watchEffect ' 函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调,失效回调会被触发 条件: 5.1.副作用即将重新执行时 5.2.侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时) 6.触发时机在'update' 生命周期之前当然你可以通过配置更改 watchEffect( () => { /* ... */ }, { flush: 'post'// post 是之后 默认是pre, sync,这将强制效果始终同步触发 } ) ~~~ >[danger] ##### 简单案例 ~~~ 1.下面案例运行后控制台打印1,即使'watchEffect' 里面没有任何指定的属性依然会执行,这也和上面的3.2点 解释符合,需要先立即执行收集内部需要被简单的数据 ~~~ ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const stop = watchEffect(()=>{ console.log(1) }) return { } } } </script> ~~~ >[danger] ##### 简单案例 ~~~ 1.下面代码执行结果先打印'1 0 "w" ' 在一秒后打印 '8 1 10 "z" ',因为'watchEffect' 是立即执行因此先打印 一次,在第一次立即执行的时候收集了需要监听的响应属性,在定时器1s后执行改变了监听的响应数据, 因此'watchEffect' 被触发 -- 简单的说'watchEffect'回调里完成了自动收集响应依赖 ~~~ ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect(()=>{ console.log(1,count.value,str.value) count.value = 100 }) setTimeout(()=>{ count.value = 10 str.value = "z" },1000) return { count } } } </script> ~~~ >[danger] ##### 调用返回值以停止侦听 ~~~ 1.在一些情况下,也可以显式调用返回值以停止侦听,下面案例只打印了'1 0 "w" ' 在一秒后并没有打印 '8 1 10 "z" ' 原因手动调用了停止响应式监听 ~~~ ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect(()=>{ console.log(1,count.value,str.value) count.value = 100 }) // 停止了响应式监听 stop() setTimeout(()=>{ count.value = 10 str.value = "z" },1000) return { count } } } </script> ~~~ >[danger] ##### 解释第五条清除副作用 ~~~ 1.下面'onInvalidate '在没有放开下面两条注释条件任意一个的时候'onInvalidate ' 不会执行,但是 放开其中任意一个条件'onInvalidate ' 都将会执行 2.说明'onInvalidate ' 会在触发监听属性改变前执行即,如果放开下面1s后执行的注释控制台打印的结果: 1 0 "w" 执行回调 1 10 "z" 所以这个'onInvalidate ' 触发跟你放的顺序无关和上面'5.1' '5.2' 条件有关 ~~~ ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect((onInvalidate )=>{ console.log(1,count.value,str.value) onInvalidate(()=>{ console.log('执行回调') }) }) // 停止了响应式监听 --侦听器被停止触发回调 // stop() // ---------------------- // setTimeout(()=>{ // 副作用即将重新执行时 // count.value = 10 // str.value = "z" // },1000) return { count } } } </script> ~~~ >[danger] ##### 侦听器调试 ~~~ 1.onTrack 和 onTrigger 只能在开发模式下工作。 onTrack 和 onTrigger 选项可用于调试侦听器的行为。 onTrack 将在响应式 property 或 ref 作为依赖项被追踪时被调用。 onTrigger 将在依赖项变更导致副作用被触发时被调用。 这两个回调都将接收到一个包含有关所依赖项信息的调试器事件。建议在以下回调中编写 debugger 语句来检查依赖关系: ~~~ ~~~ watchEffect( () => { /* 副作用 */ }, { onTrigger(e) { debugger } } ) ~~~ >[success] ## watch ~~~ 1.与watch配置功能一致 2.监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调 3.默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次 4.通过配置deep为true, 来指定深度监视 5.watch 与 watchEffect 在手动停止,副作用无效 (将 onInvalidate 作为第三个参数传递给回调),flush timing 和 debugging 方面有相同的行为 ~~~ >[danger] ##### 案例 ~~~ 1.如果是ref 定义监听直接监听属性即可,如果是reactive 包裹的需要用返回的是getter 函数指定监听 ~~~ ~~~ // 侦听一个 getter const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } ) // 直接侦听ref const count = ref(0) watch(count, (count, prevCount) => { /* ... */ }) ~~~ >[danger] ##### 监听一组 ~~~ 1.下面案例打印结果 ["新的count", "新str"]  ["老count", "老str"],当然你也可以二次结构将值取出了例如 watch([count,str],([newCount,newStr],[oldCount,oldStr])=>{}) ~~~ ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watch} from 'vue' export default { setup() { const count = ref('老count') const str = ref('老str') watch([count,str],(newVaul,oldVaul)=>{ console.log(newVaul,oldVaul) }) setTimeout(()=>{ count.value = '新的count' str.value = "新str" },1000) return { count } } } </script> ~~~ >[danger] ##### watch 与 watchEffect ~~~ const stop = watch([count,str],(newVaul,oldVaul,onInvalidate)=>{ console.log(newVaul,oldVaul) onInvalidate(()=>{ }) },{ onTrigger(e){} }) stop() ~~~ >[success] # 总结watch 与 watchEffect 不同 ~~~ 1.watch函数 1.1.监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调 1.2.默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次 1.3.通过配置deep为true, 来指定深度监视 2.watchEffect函数 2.1.不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据 2.2.默认初始时就会执行第一次, 从而可以收集需要监视的数据 2.3.监视数据发生变化时回调 ~~~