watchAPI 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。watch
需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。
-
与 watchEffect 相比,
watch
允许我们:- 惰性地执行副作用;
- 更具体地说明应触发侦听器重新运行的状态;
- 访问被侦听状态的先前值和当前值。
API:
watch(WatcherSource, Callback, [WatchOptions])
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean
}
watch单个源:
setup() {
const showNav = ref(true);
watch(
showNav,
() => {
const [search] = document.getElementsByClassName('search');
if (search) search.style.height = !showNav.value ? '100%' : 'calc(100% - 0.7rem)';
},
{ immediate: true },
);
}
watch多个源:
import { useStore } from 'vuex';
import { ref } from 'vue';
setup() {
const store = useStore();
const showNav = ref(true);
const user = JSON.parse(window.localStorage.getItem('user') || null);
watch(
[showNav, user],
([sNVal, uNVal]) => {
const [search] = document.getElementsByClassName('search');
if (search) search.style.height = !sNVal.value ? '100%' : 'calc(100% - 0.7rem)';
if (uNVal && Object.keys(uNVal).length) {
setInterval(() => {
store.dispatch('announce/notification');
}, 6000);
}
},
{ immediate: true },
);
}
在 setup 中 watch props 的某个属性:
export default {
props: {
partParams: [Object]
},
setup (props) {
/** resAPI */
function getDictionaryInfoByCon ({ dicName, dicType }) {
const payload = {
dicName,
dicType,
page: pageContain.page,
rows: pageContain.rows
};
fetchDictionaryInfoByCon(payload).then(({ data: resData }) => {
const { status, obj, total } = resData;
if (status === 200) {
const info = obj.map((i, index) => ({ key: index, dicId: i.dicId, dicName: i.dicName, dicType: i.dicType, dicKey: i.dicKey, dicValue: i.dicValue, dicSymbol: i.dicSymbol }));
preDataSource.value = Object.assign([], info);
dataSource.value = Object.assign([], info);
pageContain.total = total;
}
});
}
/** focal point */
watch(() => props.partParams, (newVal) => {
getDictionaryInfoByCon({ dicName: newVal.dicName, dicType: newVal.dicType });
}, { immediate: true });
}
}
监听数组变化 :
- 将数组定义为响应式数据ref时,如果不加上deep:true,watch是监听不到值的变化的。
- 将数组定义为响应式数据ref,加上deep:true,watch可以检测到数据的变化,但老值和新值一样,即不能获取老值。
- 将数组定义为响应式对象时,不作任何处理,watch可以检测到数据的变化,但老值和新值一样。
- 将数组定义为响应式对象时,把watch的数据源写成函数的形式并通过扩展运算符克隆一份数组返回,可以在监听的同时获得新值和老值。
最佳实践:把watch的数据源写成函数的形式并通过扩展运算符克隆一份数组返回,在监听的同时获得新值和老值。
setup() {
const testArr = ref([]);
const pushTest = (val) => {
testArr.value.push(val);
}
const popTest = () => {
testArr.value.pop();
}
/** 数组监听的实践- ref, 且加上deep: true */
watch(testArr, (newVal) => {
console.log('newVal', newVal);
}, { deep: true });
return { testArr, pushTest, popTest }
},
setup() {
const testArr = reactive([]);
const pushTest = (val) => {
testArr.push(val);
}
const popTest = () => {
testArr.pop();
}
/** 数组监听的最佳实践- reactive且源采用函数式返回,返回拷贝后的数据 */
watch(() => [...testArr], (newVal, oldVal) => {
console.log('newVal', newVal);
console.log('oldVal', oldVal);
});
return { testArr, pushTest, popTest }
},
监听对象变化:
- 把对象定义为响应式对象reactive时,采用函数形式的返回,如果不加上deep:true,watch是监听不到值的变化的。
- 把对象定义为响应式对象reactive时,加上deep:true,watch可以检测到数据的变化,但老值和新值一样,即不能获取老值。
- 把watch的数据源写成函数的形式并通过深拷贝克隆(这里用了lodash库的深拷贝)一份对象返回,在监听的同时获得新值和老值。
最佳实践:把数据定义成响应式对象reactive,把watch的数据源写成函数的形式并通过深拷贝克隆一份对象返回,在监听的同时获得新值和老值。
<script>
import { reactive, watch } from 'vue';
import _ from 'lodash';
export default {
name: 'TestDemo',
setup () {
const objReactive = reactive({user: {name: 'aa', age: '18'}, brand: 'Channel'});
/** 对象深度监听的最佳实践- reactive且源采用函数式返回,返回深拷贝后的数据 */
watch(() => _.cloneDeep(objReactive), (newVal, oldVal) => {
console.log('newVal', newVal);
console.log('oldVal', oldVal);
})
}
}
</script>
参考文献: