import { watchEffect, ref } from "vue";
import { onEffectCleanup } from "@vue/reactivity";
const flag = ref(true);
watchEffect(() => {
if (flag.value) {
const timer = setInterval(() => {
// 做一些事情
console.log("do something");
}, 200);
onEffectCleanup(() => {
clearInterval(timer);
});
}
});
上面这个例子在watchEffect
中会去注册一个循环调用的定时器,如果不使用onEffectCleanup
,那么我们就需要在beforeUnmount
钩子函数中去清理定时器。
但是有了onEffectCleanup
后,将clearInterval
放在他的回调中就可以了。当组件卸载时会自动执行onEffectCleanup
传入的回调函数,也就是会执行clearInterval
清除定时器。
还有一点值得注意的是onEffectCleanup
函数目前没有在vue
包中暴露出来,如果你想使用可以像我这样从@vue/reactivity
包中导入onEffectCleanup
函数。
新增base watch函数
我们之前使用的watch
函数是和Vue组件以及生命周期一起实现的,他们是深度绑定的,所以watch
函数代码的位置在vue源码中的runtime-core
模块中。
但是有的场景中我们只想使用vue的响应式功能,也就是vue源码中的reactivity
模块,比如小程序vuemini
。为此我们不得不将runtime-core
模块也导入到项目中,或者像vuemini
一样去手写一个watch函数。
在3.5版本中重构了一个base watch
函数,这个函数的实现和vue组件没有一毛钱关系,所以他是在reactivity
模块中。详情可以查看我之前的文章:Vue3.5新增的baseWatch让watch函数和Vue组件彻底分手
还有一点就是这个base watch
函数对于普通开发者来说没有什么影响,但是对于一些下游项目,比如vuemini
来说是和受益的。
新增onWatcherCleanup函数
和前面的onEffectCleanup
函数类似,在组件卸载之前或者下一次watch
回调执行之前会自动调用onWatcherCleanup
函数,同样有了这个函数后你就不需要在组件的beforeUnmount
钩子函数去统一清理一些timer了。比如下面这个场景:
import { watch, ref, onWatcherCleanup } from "vue";
watch(flag, () => {
const
timer = setInterval(() => {
// 做一些事情
console.log("do something");
}, 200);
onWatcherCleanup(() => {
console.log("清理定时器");
clearInterval(timer);
});
});
和onEffectCleanup
函数不同的是我们可以从vue中import导入onWatcherCleanup
函数。
新增pause和resume方法
有的场景中我们可能想在“一段时间中暂停一下”,不去执行watch
或者watchEffect
中的回调。等业务条件满足后再去恢复执行watch
或者watchEffect
中的回调。在这种场景中pause
和resume
方法就能派上用场啦。
下面这个是watchEffect
的例子,代码如下:
<button @click="count++">count++button>
<button @click="runner2.pause()">暂停button>
<button @click="runner2.resume()">恢复button>
</template>
watch的deep选项支持传入数字
在以前deep
选项的值要么是false
,要么是true
,表明是否深度监听一个对象。在3.5中deep
选项支持传入数字了,表明监控对象的深度。
比如下面的这个demo:
const obj1 = ref({
a: {
b: 1,
c: {
d: 2,
e: {
f: 3,
},
},
},
});
watch(
obj1,
() => {
console.log("监听到obj1变化");
},
{
deep: 3,
}
);
function changeDeep3Obj() {
obj1.value.a.c.d = 20;
}
function changeDeep4Obj() {
obj1.value.a.c.e.f = 30;
}
在上面的例子watch
的deep
选项值是3,表明监听到对象的第3层。
changeDeep3Obj
函数中就是修改对象的第3层的d
属性,所以能够触发watch
的回调。
而changeDeep4Obj
函数是修改对象的第4层的f
属性,所以不能触发watch
的回调。
SSR服务端渲染
服务端渲染SSR主要有这几个部分:新增useId
函数、Lazy Hydration 懒加载水合、data-allow-mismatch
新增useId
函数
有时我们需要生成一个随机数塞到DOM元素上,比如下面这个场景:
<label :htmlFor="id">Do you like Vue3.5?label>
<input type="checkbox" name="vue3.5" :id="id" />
</template>
Lazy Hydration 懒加载水合异步组件现在可以通过 defineAsyncComponent() API 的 hydrate 选项来控制何时进行水合。(欧阳觉得这个普通开发者用不上,所以就不细讲了)
data-allow-mismatch
SSR中有的时候确实在服务端和客户端生成的html不一致,比如在DOM上面渲染当前时间,代码如下:
<div>当前时间是:{{ new Date() }}div>
</template>
这种情况是避免不了会出现前面useId
例子中的那种警告,此时我们可以使用data-allow-mismatch
属性来干掉警告,代码如下:
<div data-allow-mismatch>当前时间是:{{ new Date() }}div>
</template>
Custom Element 自定义元素改进
这个欧阳也觉得平时大家都用不上,所以就不细讲了。
Teleport组件新增defer延迟属性
Teleport
组件的作用是将children中的内容传送到指定的位置去,比如下面的代码:
"target"><
/div>
被传送的内容Teleport>
文案被传送的内容
最终会渲染在id="target"
的div元素中。
在之前有个限制,就是不能将放在
Teleport
组件的后面。
这个也很容易理解DOM是从上向下开始渲染的,如果先渲染到Teleport
组件。然后就会去找id的值为target
的元素,如果找不到当然就不能成功的将Teleport
组件的子节点传送到target
的位置。
在3.5中为了解决这个问题,在Teleport
组件上新增了一个defer
延迟属性。
加了defer
延迟属性后就能将target
写在Teleport
组件的后面,代码如下:
"#target">被传送的内容</Teleport>
div>
defer
延迟属性的实现也很简单,就是等这一轮渲染周期结束后再去渲染Teleport
组件。所以就算是target
写在Teleport
组件的后面,等到渲染Teleport
组件的时候target
也已经渲染到页面上了。
useTemplateRef
函数
vue3中想要访问DOM和子组件可以使用ref进行模版引用,但是这个ref有一些让人迷惑的地方。
比如定义的ref变量到底是一个响应式数据还是DOM元素?
还有template中ref属性的值明明是一个字符串,比如ref="inputEl"
,怎么就和script中同名的inputEl
变量绑到一块了呢?