vue3源码学习(四)watch 源码学习
vue3源码学习(四) —— watch 源码学习
watch 源码学习回顾、复习。
watch 源码学习
- vue3源码学习(四) —— watch 源码学习
- 一、实现 watch 基础函数
- 1、汇总概念
- 2、实现 watch 函数
- 3、实现watch的immediat、deep 选项
- 二、完整代码
vue3.x使用示例:
import { reactive, effect, computed, watch } from './reactivity.esm.js';
const state = reactive({name: 'zhangsan',age: 32
})// 数据变化后调用回调函数
// 常见两种方式 直接监控某个对象 或者 监控某个具体的值// 常见写法一: 监听对象
// 直接监听对象, 不建议 深度监听
watch(state, (newVal, oldVal) => {console.log('监听到数据变化了');
})// 常见写法二: 监听具体的值
watch(() => state.name,(newVal, oldVal) => {console.log('name变化了', newVal, oldVal);
})
watch使用方法多种多样在此不做赘述,如需了解点击链接学习 watch其他写法
一、实现 watch 基础函数
1、汇总概念
- 数据变化后调用回调函数
- 常见两种方式 直接监控某个对象 或者 监控某个具体的值
- watch 就是effect, 状态会收集watch effect, 属性变化后会触发scheduler
- 默认的effect会在组件中用 -> 渲染effect 计算属性effect 用户effect(watch) 三种effect
- 组件渲染effect 和 计算属性effect 是同步执行的, 用户effect(watch) 是异步执行的
2、实现 watch 函数
分析:参数1:函数或者对象, 参数二:回调函数
export function isReactive(target) {// 如果访问的是__v_isReactive属性,返回truereturn !!(target && target[ReactiveFlags.IS_REACTIVE])
}// 递归收集依赖
// 通过递归来访问对象的每一个key从而实现收集依赖
function traverse (source, set=new Set()){if(isObject(source)){if(set.has(source)){return source}else{for(let key in source){// 考虑循环引用的问题, 采用set来存储已经访问过的对象traverse(source[key])}}}return source
}// 这里只对immediate,deep这两个参数做处理
export function watch(source, cb){let oldValue; //记录旧值// 首先判断source类型且是不是响应式类型let getter;if(isReactive(source)){getter = () => traverse(source) }else if(isFunction){getter = source;}const job = () => {let newValue = effect.run()cb(newValue, oldValue)oldValue = newValue}// watch本身也就是一个effectlet effect = new ReactiveEffect(getter, job)oldValue = effect.run() // 默认执行一次记录老值老值
}
3、实现watch的immediat、deep 选项
export function watch (source, cb, { immediate = false, deep = false } = {}) {let oldValue;let getter;if(isReactive(source)){// 最终都处理成函数getter = () => traverse(source); // 稍后直接调用run的时候会执行此函数,直接返回对象,只访问属性才能收集依赖}else if(isFunction(source)){if(deep){getter = () => traverse(source());}else{getter = source;}}const job = () => {// 内部要调用cb也就是watch的回调方法let newValue = effect.run();cb(newValue, oldValue);oldValue = newValue;}let effect = new ReactiveEffect(getter, job)// immediate为true时, 立即执行回调if(immediate){return job();}oldValue = effect.run(); // 默认执行一次保留老值
}
二、完整代码
import { isFunction, isObject } from "@vue/shared";
import { ReactiveEffect } from "./effect";
import { isReactive } from "./reactive";// 递归收集依赖
const traverse = (source, set=new Set()) => {if(isObject(source)){if(set.has(source)){return source;}else{// 考虑循环引用的问题, 采用set来存储已经访问过的对象set.add(source);for(const key in source){traverse(source[key], set)}}}return source;
}export function watch (source, cb, { immediate = false, deep = false } = {}) {let oldValue;let getter;if(isReactive(source)){// 最终都处理成函数getter = () => traverse(source); // 稍后直接调用run的时候会执行此函数,直接返回对象,只访问属性才能收集依赖}else if(isFunction(source)){if(deep){getter = () => traverse(source());}else{getter = source;}}const job = () => {// 内部要调用cb也就是watch的回调方法let newValue = effect.run();cb(newValue, oldValue);oldValue = newValue;}let effect = new ReactiveEffect(getter, job)// immediate为true时, 立即执行回调if(immediate){return job();}oldValue = effect.run(); // 默认执行一次保留老值
}