4.7 watch 的实现原理
watch本质上就是使用了effect以及options.scheduler
定义watch函数:
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
effect(
() => source.foo,
{
scheduler(){
// 回调函数
cb()
}
}
)
}
watch接收两个参数分别是source和cb
source
是一个对象,我们希望监听它的foo
属性的变化。cb
是一个回调函数,当source.foo
发生变化时,这个回调函数会被执行。
watch的使用:
// 使用watch函数
watch(obj , () => {
console.log('值发生改变')
})
// 修改响应数据的值,导致回调函数执行
obj.foo++
值发生改变
**改为更加通用:**除了foo发生改变,其他的发生改变也可以
修改思路:使用函数对传入的对象循环读取,traverse函数:传入对象以及一个set集合(存储已经访问过的),判断类型,类型过关就进行遍历。
// traverse:值以及读取放入的set
function traverse(value , seen = new Set){
// 如果要读取的数据是原始值,或者已经读取过了,那么什么都不做
if(typeof value !== 'object' || value === null || seen.has(value)){
return;
}
// 暂时仅考虑value是一个对象,遍历value
for(const k in value){
traverse(value[k] , seen);
}
}
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
effect(
// traverse
() => traverse(source),
{
scheduler(){
// 回调函数
cb()
}
}
)
}
壮大watch—getter函数:
修改思路:定义一个getter,如果source是函数类型直接使用getter函数,如果不是则递归调取
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
// 定义getter
let getter
if(typeof source === 'function'){
getter = source
}else {
getter = () => traverse(source)
}
effect(
() => getter(),
{
scheduler(){
// 回调函数
cb()
}
}
)
}
重要功能—新旧值
修改思路:这时候要拿到effect的返回参数,返回参数就是oldval,最核心的改动就是添加了懒加载lazy创建了一个懒加载effect,需要的时候才会执行,在值发生改变时,更新新值和旧值
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
// 定义getter
let getter
if(typeof source === 'function'){
getter = source
}else {
getter = () => traverse(source)
}
// 定义新旧值
let newVal , oldVal
const effectFn = effect(
() => getter(),
{
lazy: true,
scheduler(){
// 值发生改变会发生,此时就有新值了
newVal = effectFn()
// 回调函数,传入新旧值
cb(oldVal , newVal)
// 一定要记得更新旧值
oldVal = newVal
}
}
)
// 调用effectFn就是旧值
oldVal = effectFn();
}
source以及回调函数
function watch(source , cb) {
effect(
// traverse
() => traverse(source),
{
scheduler(){
// 回调函数
cb()
}
}
)
}
**壮大watch---getter函数:**
修改思路:定义一个getter,如果source是函数类型直接使用getter函数,如果不是则递归调取
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
// 定义getter
let getter
if(typeof source === 'function'){
getter = source
}else {
getter = () => traverse(source)
}
effect(
() => getter(),
{
scheduler(){
// 回调函数
cb()
}
}
)
}
**重要功能---新旧值**
修改思路:这时候要拿到effect的返回参数,返回参数就是oldval,最核心的改动就是添加了懒加载lazy创建了一个懒加载effect,需要的时候才会执行,在值发生改变时,更新新值和旧值
// watch函数:传入参数source以及回调函数
function watch(source , cb) {
// 定义getter
let getter
if(typeof source === 'function'){
getter = source
}else {
getter = () => traverse(source)
}
// 定义新旧值
let newVal , oldVal
const effectFn = effect(
() => getter(),
{
lazy: true,
scheduler(){
// 值发生改变会发生,此时就有新值了
newVal = effectFn()
// 回调函数,传入新旧值
cb(oldVal , newVal)
// 一定要记得更新旧值
oldVal = newVal
}
}
)
// 调用effectFn就是旧值
oldVal = effectFn();
}