Vue3学习笔记之watchEffect
最近在看 Vue3 的⼀些新 feature,顺道学习了⼀些 hooks 编程的思想,感觉挺有启发的。今天就以 watchEffect 这个很⼩的 case 为例,开启我的 Vue3 学习笔记。
Vue2 v.s. Vue3
对所有初学者来说,Vue2 到 Vue3 最直观的改变就是 Composition API——⼏乎所有的 Vue2 options ⽅法都被放到了 setup 函数⾥:
+ import { onMounted, reactive, watchEffect } from 'vue'
export default {
name: "App",
+  setup( props ) {
+    const state = reactive({ /*...*/ });
+    onMounted(() => { /*...*/ });
+    watchEffect(() => { /*...*/ });
+    return { state };
+  },
-  data: () => ({ state: /*...*/ }),
-  mounted(){ /*...*/ },
-  watch: { /*...*/ },
};
复制代码
这是⼀个⽐较⼤的风格转变,通俗来说,就是从基于对象的编程(OOP)转向了函数式编程(FP)。
函数式编程
初学者可能分辨不清 OOP 和 FP 的区别。⼤家注意看 onMounted 和 watchEffect ⽅法的参数——箭头函数,⼤致能体会到不同之处了。
OOP 的特点是:对象(或 class)是数据(variable)和逻辑(methods)的封装。在 Vue2 时代,我们经常写如下代码:
// vue2
export {
data: () => ({count: 1}),
methods: {
message: (prefix) => `${prefix} ${unt}`,
},
watch: {
count() {
console.log( ssage('Count is') );
};
}
}
复制代码
Vue2 的内部实现⽐较复杂,不过对外表现的编程模式基本就是:对象调⽤⾃⼰的数据和⽅法——this + . 操作。所以在 Vue2 时代,我们通常会把相关的数据和操作写在同⼀个对象⾥。但是到了 Vue3 的 setup ⾥,你⼏乎不会⽤到 this 了;变成了让函数来调⽤对象或是另⼀个函数——就是 FP 的特点了。
这些其他改变就是所谓的副作⽤(side effect)。在 FP 的世界⾥,我们不能向 Vue2 那样简单地调⽤全局插件了(this.$t、this.$router、this.$store……);⽽是通过间接的⼿段——即通过其他函数调⽤——包含副作⽤。Vue3 就提供了⼀个通⽤的副作⽤钩⼦(hook)叫做watchEffect(从名字上也可见⼀斑),就是我们今天的主⾓了。
watchEffect
兜兜转转,我们再来介绍⼀下 watchEffect 的⽤法,借助 typescript,我们可以很清晰地看到该函数的定义:
类型定义
function watchEffect(
effect: (onInvalidate: InvalidateCbRegistrator) => void,
options?: WatchEffectOptions
): StopHandle;
interface WatchEffectOptions {
flush?: "pre" | "post" | "sync";
onTrack?: (event: DebuggerEvent) => void;
onTrigger?: (event: DebuggerEvent) => void;
}
interface DebuggerEvent {
effect: ReactiveEffect;
target: any;
type: OperationTypes;
key: string | symbol | undefined;
}
type InvalidateCbRegistrator = (invalidate: () => void) => void;
type StopHandle = () => void;
复制代码
第⼀个参数
watchEffect ⾃⼰是函数,它的第⼀个参数——effect——也是函数(函数是⼀等公民,可以⽤在各个地⽅)。effect,顾名思义,就是包含副作⽤的函数。如下代码中,副作⽤函数的作⽤是:当 count 被访问时,旋即在控制台打出⽇志。
// Vue3
import { ref, watchEffect } from "vue";
export default {
setup() {
const count = ref(1);
const effect = () => console.log(count.value);
watchEffect(effect);
const的作用setTimeout(() => count.value++, 1000);
return { count };
},
};
复制代码
如上代码会打印出0和1,0是出于 Vue 响应式设计,在响应式元素(count)依赖收集阶段会运⾏⼀次 effect 函数;0是来⾃ setTimeout ⾥对 count 修改的操作。
清除副作⽤(onInvalidate )
⼤家注意到没有?watchEffect 的第⼀个参数——effect函数——⾃⼰也有参数:叫onInvalidate,也是⼀个函数,⽤于清除 effect 产⽣的副作⽤。(⽽且 onInvalidate 的参数也是函数,哈哈!)
*p.s. FP 就是这样,函数嵌套函数;初学者可能有点晕,习惯就好*
onInvalidate 被调⽤的时机很微妙:它只作⽤于异步函数,并且只有在如下两种情况下才会被调⽤:
1. 当 effect 函数被重新调⽤时
2. 当被注销时(如组件被卸载了)
如下代码中,onInvalidate 会在 id 改变时或停⽌侦听时,取消之前的异步操作(asyncOperation):
import { asyncOperation } from "./asyncOperation";
const id = ref(0);
watchEffect((onInvalidate) => {
const token = asyncOperation(id.value);
onInvalidate(() => {
// run if id has changed or watcher is stopped
token.cancel();
});
});
复制代码
返回值(停⽌侦听)
副作⽤是随着组件加载⽽发⽣的,那么组件卸载时,就需要清理这些副作⽤。watchEffect 的返回值——StopHandle依旧是⼀个函数——就是⽤在这个时候。如下 stopHandle 可以在 setup 函数⾥显式调⽤,也可以在组件被卸载时隐式调⽤。
setup() {
const stopHandle = watchEffect(() => {
/* ... */
});
// 之后
stopHandle();
}
复制代码
第⼆个参数
watchEffect 还有第⼆个参数叫 options,类型是WatchEffectOptions,⼀个很复杂的接⼝。虽然很少能被⽤到吧,但也在这⾥快速提⼀下。
第⼆个参数的主要作⽤是指定调度器,即何时运⾏副作⽤函数。⽐如,你希望副作⽤函数在组件更新前发⽣,可以将 flush 设为 'pre'(默认是 'post')。还有 WatchEffectOptions 也可以⽤于 debug:onTrack 和 onTrigger 选项可⽤于调试⼀个侦听器的⾏为(当然只开发阶段有效)。
// fire before component updates
watchEffect(
() => {
/
* ... */
},
{
flush: "pre",
onTrigger(e) {
debugger;
},
}
);
复制代码
注意点
watchEffect 会在 Vue3 开发中⼤量使⽤,这⾥说⼏个注意点:
1. 如果有多个负效应,不要粘合在⼀起,建议写多个 watchEffect。
watchEffect(() => {
setTimeout(() => console.log(a.val + 1), 1000);
setTimeout(() => console.log(b.val + 1), 1000);
});
复制代码
这两个 setTimeout 是两个不相关的效应,不需要同时监听 a 和 b,分开写吧:
watchEffect(() => {
setTimeout(() => console.log(a.val + 1), 1000);
});
watchEffect(() => {
setTimeout(() => console.log(b.val + 1), 1000);
});
复制代码
2. watchEffect 也可以放在其他⽣命周期函数内
⽐如你的副作⽤函数在⾸次执⾏时就要调⽤ DOM,你可以把他放在 onMounted 钩⼦⾥:
onMounted(() => {
watchEffect(() => {
// access the DOM or template refs
});
}
如果⼤家想学习前端⽅⾯的技术,我把我多年的经验分享给⼤家,还有⼀些学习资料,分享Q:1046097531

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。