共计 4039 个字符,预计需要花费 11 分钟才能阅读完成。
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>
正文完