第四章 细节处理

1.手动调用effect

如果我们在数据更新之后第一时间不想去更新视图,我们在官方的vue中就可以通过effect中的一个配置来实现,即effect.scheduler。因此我们也需要实现这个功能。

1. 首先我们在effect函数调用时传入第二个参数作为一个配置对象,并将这个配置对象合并到_effect中。

export function effect(fn, options?) {

    1.// 创建一个响应式effect,数据变化后可以重新执行

    const _effect = new ReactiveEffect(fn,() => {

        _effect.run()

    })

    _effect.run() // 默认执行一次


    // 为effect挂载options

    if(options) {

        Object.assign(_effect,options) // 覆盖掉之前的

    }

}

由于我们前天在依赖调用是调用的是effect的scheduler,因此这个操作就会覆盖掉我们设置的scheduler。从而执行用户自定义的逻辑。

2. 接下来我们需要将effect的run方法作为值暴露出去为用时使用,当用户根据自身情况调用,同时在run函数上挂载当前的effect实例。

# effect.ts

function effect(fn, options?) {

  ...

    // 将effect的run方法暴露给外部

  const runner = _effect.run.bind(_effect)

  runner.effect = _effect

  return runner

}

2.嵌套调用

我们先来看这样一段代码

const user = reactive({

      name:'张三',

      age:18,

      state:{

          title: 1

      }

  })


  effect(() => {

        document.body.innerHTML = user.name

        user.name = Math.random()

  })

如上所示的代码,当effect执行之后在函数内部即读取了属性,又设置了属性值,此时就会连续触发effect的收集与执行,因此我们需要在依赖执行前判断当前依赖是否是正在执行,如果正在执行,则无需再触发。

我们先为ReactiveEffect设置一个属性咱们用于保存当前正在运行的effect数量, _running

# effect.ts

// 每次依赖执行前_running + 1,执行完后 - 1

 try {

    currentEffect = this

// 这里为了避免无用的effect依赖,每次触发收集之前将原先的依赖表清空

    preClearEffect(this)

    this._running++ // 记录当前正在运行的effect数量

    return this.fn()

} finally {

    // 删除以前依赖表中多余的依赖

    postDepEffect(this)

    this._running-- // 记录当前正在运行的effect数量

    currentEffect = lastEffect

}

// 在触发依赖的位置判断是否需要触发

// 触发依赖

export function triggerEffect(deps) {

    for(const effect of deps.keys())  {

        if(effect.scheduler) {

            // 判断当前是否已经有正在执行的effect

            if(effect._running === 0) {

                effect.scheduler() // 默认相当于调用了effect.run()

            }
        }
    }
}

3.深度代理

当前reactive的实现中,如果数据是对象类型,则只对第一层进行代理,如果对象内部还有对象,则不会进行代理。

但是如果是对象嵌套对象形式,根据源码的逻辑,需要在读取到该对象的子对象是才对其子对象进行代理。因此我们需要在读取属性时判读所读取的属性是否是对象,如果是则进行代理。

# baseHandler.ts

export const mutableHandlers:  ProxyHandler<any> = {

  get(target,key,recevier) {

        // 判断当前是否已经是响应式对象

        if(key === ReactiveFlags.IS_REACTIVE) {

            return true

        }

        // 取值时,需要将响应式属性与effect进行绑定 - 依赖收集

        track(target,key) // 收集依赖

        const result  = Reflect.get(target,key,recevier)

        // 深度代理

        if(isObject(result)) {

            return reactive(result)

        }

        return result

    },

    set: ...

}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值