高阶指南
响应式状态
- reactive: 深层转化-用于转化对象,data 选项中返回的对象
- ref 创建一个独立的响应式对象,通过 value 属性访问其内部值
- ref 在模板中使用时,将会自动展开为原始值
- 当 ref 存在于 reactive 参数对象中时,自动展开为原始值。reactive 的参数一定得是一个原始对象。不能是数组,或者 map 这种集合类型
setup() {
const myRef = ref(0)
const myReactive = reactive({
myRef})
console.log(myReactive.myRef)
myReactive.myRef = 1
console.log(myRef.value)
}
- reactive 创建的响应式对象发生解构时,会丢失其响应式特性。解决方法是先通过 toRefs 转化为 一系列 Ref,再进行解构(toRef 是解构一个,需要两个参数,toRefs 处理一组。)
- 使用 readonly 防止响应式对象的修改
响应式状态侦听、计算
computed
- 计算属性 computed,接受一个函数(此时是 getter),也可以接受一个 包含 get 和 set 的对象参数。按照 Ref 的方式访问其值
watchEffect
- watchEffect 在依赖变更时重新执行副作用。特点是立即执行,并且无需代入依赖参数。返回一个停止监听的函数
setup() {
const myRef = ref(0);
onMounted(() => {
setInterval(() => {
myRef.value = myRef.value + 1;
}, 1000);
});
const stopWatch = watchEffect(() => {
console.log(myRef.value);
if (myRef.value > 3) {
stopWatch();
}
});
- 但是 watchEffect 中如果是一个异步的副作用的话,就算我们通过返回函数去停止了监听,但是异步动作仍然是触发了的。所以需要一个函数,这个函数在 watchEffect 清除的同时,又能清除异步副作用。watchEffect 采用 onInvalidate 参数解决这个问题
- ***onInvalidate 参数是为了在停止监听的时候,去除其未完成的副作用
- ***类似于一个监听,监听到了停止事件,那么就会就会执行这个之前收集到的函数
- ***通常在副作用执行之前,设置这个值
当没有设置 onInvalidate 监听函数的时候,到 4 停止。因为停止监听的时候,effect 已经执行
setup() {
let timer = null;
const data = ref(0);
const effect = () => {
timer = setTimeout(() => {
data.value = data.value + 1;
}, 1000);
};
const stop = watchEffect((onInvalidate) => {
effect();
if (data.value === 3) {
stop();
}
});
return {
data };
},
当设置了 onInvalidate 监听,那么到 3 就会停止。在函数里写上清除副作用的函数
setup() {
let timer = null;
const data = ref(0);
const effect = () => {
timer = setTimeout(() => {
data.value = data.value + 1;
}, 1000);
};
const stop = watchEffect((onInvalidate) => {
onInvalidate(() => {
clearTimeout(timer);
});
effect();
if (data.value === 3) {
stop();
}
});
return {
data };
},
- 当一个用户自定义的更新进入队列时,默认情况下会在 组件更新 之前执行。要想在组件更新之后执行一些副作用,可以代入一个带有 flush 选项的附加 options 对象(默认为‘pre’)
<template>
<h1 v-for="item in data" :key="item">{
{ item }}h1>
template>
setup() {
const data = ref([]);
const index = ref(0);
onMounted(() => {
setInterval(() => {
index.value = index.value + 1;
data.value[index.value] = index.value;
}, 2000);
});
watchEffect(
() => {
const idx = index.value;
const dom = document.getElementsByTagName("h1");
console.log(dom[idx]);
},
);
return {
data };
},
- 侦听器调试:onTrack, onTrigger,都接受一个包含有关所依赖项信息的调试器事件。并且都只能在开发模式下工作
watchEffect(
() => {
const idx = index.value;
const dom = document.getElementsByTagName("h1");
console.log(dom[idx]);
},
{
flush: "post",
onTrack(e) {
console.log(e.target.value);
},
}
);
watch
- vue3 watch API 完全等同于 vue2 watch,写法和参数略有不同
- watch 需要侦听特定数据源。在回调函数中执行副作用
- watch 与 watchEffect 对比起来,watch 是惰性的
- watch 侦听可以是函数形式的 reactive 的值,也可以是 ref,computed 等
- watch 回调函数里可以直接拿到 ref 的原始值。
- 监听 reactive 时,要在函数内返回要监听的值。回调函数的参数是监听的那个值,而不是整个 reactive 构造的 proxy
- 与 watchEffect 共享停止侦听,清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传入)、副作用刷新时机和侦听器调试行为
监听一个数据源
setup() {
const index = ref(0);
onMounted(() => {
setInterval(() => {
index.value = index.value + 1;
}, 2000);
});
watch(index, (val, preVal) => {
console.log("current", val);
console.log("pre value", preVal);
});
return {
index };
},
监听多个数据源
setup() {
const index = ref(0);
const char = ref(100);
onMounted(() => {
setInterval(() => {
index.value = index.value + 1;
char.value--;
}, 2000);
});
watch([index, char], ([index, char], [preIndex, preChar]) => {
console.log("current index", index);
console.log("current char", char);
console.log("--------");
console.log("pre index", preIndex);
console.log("pre char", preChar);
console.log("-------next loop--------------");
});
return {
index, char };
监听 reactive
setup() {
const index = reactive({
value: 0,
});
onMounted(() => {
setInterval(() => {
index.value = index.value + 1;
}, 2000);
});
watch(
() => index.value,
(val, preVal) => {
console.log("current index", val);
console.log("pre index", preVal);
console.log("-------next loop--------------");
}
);
const {
value } = toRefs(index);
return {
value };
},
与 reactive 共享的行为(清除副作用为例)
setup() {
const index = reactive({
value: 0,
});
let timer = null;
const effect = () => {
timer = setTimeout(() => {
index.value = index.value + 1;
console.log("interval", index.value);
}, 1000);
};
effect();
const stop = watch(
() => index.value,
(val, preVal, onInvalidate) => {
onInvalidate(() => {
clearTimeout(timer);
});
effect();
if (val === 3) {
stop();
}
}
);
const {
value } = toRefs(index);
return {
value };
},