Vue3响应式核心机制-watch

基本定义

  • 定义:在Vue 3中,watch是一个用于响应式地监听数据变化的API。它允许观察一个或多个响应式数据源,并在数据变化时执行回调函数。watch通常用于处理异步操作或复杂逻辑。

基本语法

watch的基本语法如下:

import { watch } from 'vue';

watch(source, callback, options);
  • source:需要监听的数据源,可以是一个响应式引用(ref)、计算属性(computed)或一个返回值的函数。
  • callback:数据变化时执行的回调函数,接收新值和旧值作为参数。
  • options(可选):配置对象,例如immediatedeep等。

监听单个数据源

以下是监听单个ref的示例:

import { ref, watch } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

监听多个数据源

可以通过数组形式监听多个数据源:

const count = ref(0);
const name = ref('Vue');

watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
  console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`);
});

监听对象或数组

默认情况下,watch是浅监听。如果需要深度监听对象或数组的变化,可以设置deep: true

const state = ref({ count: 0 });

watch(
  state,
  (newValue, oldValue) => {
    console.log('state changed', newValue, oldValue);
  },
  { deep: true }
);

立即执行回调

设置immediate: true可以让回调在初始化时立即执行:

watch(
  count,
  (newValue, oldValue) => {
    console.log(`count is ${newValue}`);
  },
  { immediate: true }
);

监听函数返回的值

可以通过函数返回需要监听的值:

const state = ref({ count: 0 });

watch(
  () => state.value.count,
  (newValue, oldValue) => {
    console.log(`count changed from ${oldValue} to ${newValue}`);
  }
);

停止监听

watch返回一个停止函数,调用它可以停止监听:

const stop = watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

// 停止监听
stop();

注意事项

  • watch的回调是异步执行的,数据变化后不会立即触发回调。
  • 如果需要同步响应数据变化,可以使用watchEffect
  • 避免在回调中修改监听的数据源,否则可能导致无限循环。

代码实现

import { effect, cleanup } from './effect/effect.js'

/**
 *
 * @param {*} source 响应式数据(对象或者函数)
 * @param {*} cb 执行的回调函数
 * @param {*} options 选项对象
 */
export function watch(source, cb, options) {
	// 参数归一化
	let getter = null
	if (typeof source === 'function') {
		getter = source
	} else {
		getter = () => traverse(source)
	}

	let oldValue = null,
		newValue = null

  const job = () => {
    newValue = effectFn()
    console.log('newValue', newValue, oldValue)
    cb(newValue, oldValue)
    oldValue = newValue
  }

  const effectFn = effect(() => getter(), {
		lazy: true,
		scheduler: job
	})

  if(options?.immediate) {
    job()
  } else {
    effectFn()
  }

  return () => {
    cleanup(effectFn)
  }
}

function traverse(source, seen = new Set()) {
	if (typeof source !== 'object' || source === null || seen.has(source)) return
	seen.add(source)
	for (const key in source) {
		traverse(source[key], seen)
	}
	return source
}
  • 和computed一样,先进行参数归一化,如果是对象,递归遍历对象,保证依赖能够正常收集
  • 设置变量存储旧值和新值
  • 使用effect函数的lazyscheduler,返回effectFn,进行手动控制
  • 当依赖的数据变化时,重新运行effectFn,收集依赖,并且运行回调函数,对oldValuenewValue重新赋值
  • 根据传入的immediate配置,判断是否需要立即运行依赖收集和回调函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值