computed的应用,传入一个函数:let x = computed(() => count.value + 3)
新增computed函数
- 减少一个computed函数,传入一个函数fn,返回一个蕴含可监听value值的对象
- value值置为传入函数执行的后果,再将value返回
- 因为须要实现缓存,减少dirty标记记录依赖的值是否变动,只有变动了才从新计算
- 计算赋值之后将标记置为false。只有数据没有变动,就不会再从新计算。
- 何时将dirty重置为true?在执行传入函数fn时,监听的响应式数据变动之后将dirty重置为true。
- 就是在执行notify()时,将所有的依赖进行一次播送,将工作退出队列,之后执行。notify中的dep对应的为watchEffect传入的cb,因而须要革新watchEffect。
// 传入一个函数,返回一个蕴含可监听value值的对象 let computed = (fn) => { let value // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算 let dirty = true return { get value() { if (dirty) { // 何时将dirty重置为true // 在执行fn时,监听的响应式数据变动之后将dirty重置为true // 就是在执行notify()时 // notify中的dep对应的为watchEffect传入的cb,因而须要革新watchEffect value = fn() // value值置为传入函数执行的后果 // 计算之后将标记置为false。只有数据没有变动,就不会再从新计算 dirty = false } return value } } }
革新watchEffect
- 新增effect函数,将原来watchEffect中的内容放进去
- effect中新建一个
_effect
函数,将fn额定包装了一层,用于给它增加属性,为了保障fn函数的纯正性
let active let effect = (fn, options = {}) => { // _effect 额定包装了一层,用于给它增加属性 // 为了保障fn函数的纯正性 let _effect = (...args) => { try { active = _effect return fn(...args) // 须要增加return语句用于computed函数中拿到变动之后的值 } finally { // 无论是否抛出异样最初finally都会执行 // 这句代码是在`return fn(...args)`后须要执行,因而须要放进try{}finally{}中 active = null } } _effect.options = options return _effect } // 之前的watch实现的即是watchEffect函数的性能 let watchEffect = (cb) => { /* active = cb active() active = null */ // 将原来局部的逻辑提取到effect函数中 let runner = effect(cb) runner() }
notify
函数中触发
- computed函数中须要用effect去代替fn,这样能够增加钩子函数,即传入effect中的options参数
- notify中执行钩子函数
// 传入一个函数,返回一个蕴含可监听value值的对象 let computed = (fn) => { let value // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算 let dirty = true let runner = effect(fn, { schedular() { if (!dirty) { dirty = true } } }) return { get value() { if (dirty) { // 何时将dirty重置为true // 在执行fn时,监听的响应式数据变动之后将dirty重置为true // 就是在执行notify()时 // notify中的dep对应的为watchEffect传入的cb,因而须要革新watchEffect // value = fn() // value值置为传入函数执行的后果 value = runner() // 计算之后将标记置为false。只有数据没有变动,就不会再从新计算 dirty = false } return value } } } let ref = initValue => { let value = initValue let dep = new Dep() return Object.defineProperty({}, 'value', { get() { dep.depend() return value }, set(newValue) { value = newValue // active() dep.notify() } }) }
// 触发 notify() { this.deps.forEach(dep => { queueJob(dep) // 执行钩子函数 dep.options && dep.options.schedular && dep.options.schedular() }) }
残缺代码:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <button id="add">add</button> <div id="app"></div></body><script> let active let effect = (fn, options = {}) => { // _effect 额定包装了一层,用于给它增加属性 // 为了保障fn函数的纯正性 let _effect = (...args) => { try { active = _effect return fn(...args) // 须要增加return语句用于computed函数中拿到变动之后的值 } finally { // 无论是否抛出异样最初finally都会执行 // 这句代码是在`return fn(...args)`后须要执行,因而须要放进try{}finally{}中 active = null } } _effect.options = options return _effect } // 之前的watch实现的即是watchEffect函数的性能 let watchEffect = (cb) => { /* active = cb active() active = null */ // 将原来局部的逻辑提取到effect函数中 let runner = effect(cb) runner() } let nextTick = (cb) => Promise.resolve().then(cb) // 队列 let queue = [] // 增加队列 let queueJob = (job) => { if (!queue.includes(job)) { queue.push(job) // 增加之后,将执行放到异步工作中 nextTick(flushJob) } } // 执行队列 let flushJob = () => { while (queue.length > 0) { let job = queue.shift() job && job() } } let Dep = class { constructor() { // 寄存收集的active this.deps = new Set() } // 依赖收集 depend() { if (active) { this.deps.add(active) } } // 触发 notify() { this.deps.forEach(dep => { queueJob(dep) // 执行钩子函数 dep.options && dep.options.schedular && dep.options.schedular() }) } } // 传入一个函数,返回一个蕴含可监听value值的对象 let computed = (fn) => { let value // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算 let dirty = true let runner = effect(fn, { schedular() { if (!dirty) { dirty = true } } }) return { get value() { if (dirty) { // 何时将dirty重置为true // 在执行fn时,监听的响应式数据变动之后将dirty重置为true // 就是在执行notify()时 // notify中的dep对应的为watchEffect传入的cb,因而须要革新watchEffect // value = fn() // value值置为传入函数执行的后果 value = runner() // 计算之后将标记置为false。只有数据没有变动,就不会再从新计算 dirty = false } return value } } } let ref = initValue => { let value = initValue let dep = new Dep() return Object.defineProperty({}, 'value', { get() { dep.depend() return value }, set(newValue) { value = newValue // active() dep.notify() } }) } // 应用: let count = ref(0) // computedValue 当count.value的值扭转时才变动 let computedValue = computed(() => count.value + 3) document.getElementById('add').addEventListener('click', function () { count.value++ }) let str watchEffect(() => { str = `hello ${count.value} ${computedValue.value}` document.getElementById('app').innerText = str })</script></html>