computed

  • 会基于其内部的 响应式依赖 进行缓存。
  • 只在相关 响应式依赖发生改变 时 它们才会重新求值。
  • 可以在将模板中使用的常量放在计算属性中。

watch

  • 监听数据变化,并在监听回调函数中返回数据变更前后的两个值。
  • 用于在数据变化后执行 异步操作 或者开销较大的操作。

watchEffect

在 composition API中 watchEffect会在它所依赖的数据发生改变时立即执行,并且执行结果会返回一个函数,我们称它为stop函数

,可以用于停止监听数据变化,下面是示例代码演示:

const count = ref(0)

// -> log 0
const stop = watchEffect(() => {
console.log(count.value)
}) setTimeout(()=>{
// -> log 1
count.value++
},100) // -> later
stop()

下面我们来实现以上介绍的几个composition API

  1. computed -> let x = computed(()=> count.value + 3);
  2. watch -> watch(()=> count.value, (curVal, preVal) => {}, { deep, immediate })
  3. watchEffect -> let stop = watchEffect(()=> count.value + 3)

computed

核心思路是

// 简单定义

let computed = (fn) => {
let value;
return {
get value() {
return value
}
}
} // 调用 let computedValue = computed(() => count.value + 3) // 监听
watchEffect(() => {
document.getElementById('computed').innerText = computedValue.value
});

下面我们在此基础之上实现依赖更新的操作

let computed = (fn) => {
let value;
return {
get value() {
// 5手动执行一次依赖
value = fn()
return value
}
}
}
let count = ref(1);
let computedValue = computed(() => count.value + 3) function add() {
document.getElementById('add').addEventListener('click',()=>{
count.value++
})
} add() watchEffect(() => {
document.getElementById('text').innerText = count.value
document.getElementById('computed').innerText = computedValue.value
});

依赖缓存计算

呈上页面 -html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue3 - computed</title>
</head>
<body>
<div id="app">
result:
<span id="text">0</span>
<br />
computed:
<span id="computed">0</span>
</div>
<button id="add">add</button>
</body> </html>

包含了computed的实现的完整js代码。

;(function () {
let active
/*
* @params fn -> 要执行的函数
* @params option -> 可选参数
* @return effect -> 执行watchEffect
*/
let effect = (fn, options = {}) => {
let effect = (...args) => {
try {
active = effect
// 避免了死循环
return fn(...args)
} finally {
active = null
}
} // 更新数据时也需要让schedular执行
effect.options = options return effect
} let watchEffect = function (cb) {
let runner = effect(cb)
runner()
}
// 需要有个队列来存储各项任务
let queue = []
// 通过微任务方式去执行队列中的任务
let nextTick = (cb) => Promise.resolve().then(cb)
// 将任务添加到队列
let queueJob = (job) => {
if (!queue.includes(job)) {
queue.push(job)
nextTick(flushJobs)
}
} // 执行队列中的任务
let flushJobs = () => {
let job
while ((job = queue.shift()) !== undefined) {
job()
}
} // 收集更多依赖
class Dep {
// 依赖收集,将响应依赖添加到deps中
constructor() {
this.deps = new Set()
} depend() {
if (active) {
this.deps.add(active)
}
}
// 通知所有依赖更新
notify() {
// 将任务加到队列中
this.deps.forEach((dep) => {
dep.options && dep.options.schedular && dep.options.schedular()
queueJob(dep)
})
}
} let ref = (initValue) => {
let value = initValue
let dep = new Dep() return Object.defineProperty({}, 'value', {
get() {
dep.depend()
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
} let computed = (fn) => {
let value
let dirty = true let runner = effect(fn, {
// 通过钩子函数处理dirty参数
schedular: () => {
if (!dirty) {
dirty = true
}
}
})
return {
get value() {
if (dirty) {
value = runner()
// 缓存标识
dirty = false
// 这里在dirty改变为false之后需要在依赖发生变化时候重置为true,
}
return value
}
}
} let count = ref(1)
// 同93 数据发生更新时让dirty 重置
let computedValue = computed(() => count.value + 3) function add() {
document.getElementById('add').addEventListener('click', () => {
count.value++
})
} add() watchEffect(() => {
document.getElementById('text').innerText = count.value
document.getElementById('computed').innerText = computedValue.value
})
})()

watch

// watch(()=> count.value, (curVal, preVal) => {}, { deep, immediate })

;(function () {
let active
/*
* @params fn -> 要执行的函数
* @params option -> 可选参数
* @return effect -> 执行watchEffect
*/
let effect = (fn, options = {}) => {
let effect = (...args) => {
try {
active = effect
// 避免了死循环
return fn(...args)
} finally {
active = null
}
} // 更新数据时也需要让schedular执行
effect.options = options return effect
} let watchEffect = function (cb) {
let runner = effect(cb)
runner()
}
// 需要有个队列来存储各项任务
let queue = []
// 通过微任务方式去执行队列中的任务
let nextTick = (cb) => Promise.resolve().then(cb)
// 将任务添加到队列
let queueJob = (job) => {
if (!queue.includes(job)) {
queue.push(job)
nextTick(flushJobs)
}
} // 执行队列中的任务
let flushJobs = () => {
let job
while ((job = queue.shift()) !== undefined) {
job()
}
} // 收集更多依赖
class Dep {
// 依赖收集,将响应依赖添加到deps中
constructor() {
this.deps = new Set()
} depend() {
if (active) {
this.deps.add(active)
}
}
// 通知所有依赖更新
notify() {
// 将任务加到队列中
this.deps.forEach((dep) => {
dep.options && dep.options.schedular && dep.options.schedular()
queueJob(dep)
})
}
} let ref = (initValue) => {
let value = initValue
let dep = new Dep() return Object.defineProperty({}, 'value', {
get() {
dep.depend()
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
} let watch = (source, cb, options = {}) => {
const { immediate } = options
const getter = () => {
return source()
}
let oldValue
const runner = effect(getter, {
schedular: () => applyCbk()
}) const applyCbk = () => {
let newValue = runner()
if (newValue !== oldValue) {
cb(newValue, oldValue)
oldValue = newValue
}
} // 有默认值时执行回调
if (immediate) {
applyCbk()
} else {
oldValue = runner()
}
} let count = ref(1) function add() {
document.getElementById('add').addEventListener('click', () => {
count.value++
})
} add() watch(
() => count.value,
(newValue, oldValue) => {
console.log(newValue, oldValue)
},
{ immediate: true }
)
})()

参数1响应式更新,参数2使用schedular执行回调,参数3 如果存在时就默认执行回调2

watchEffect

  • stop方法的实现
  • 数组API响应式执行依赖更新
  • Vue.set的实现,数组索引加入代理中
// let stop = watchEffect(()=> count.value + 3)

;(function () {
let active
/*
* @params fn -> 要执行的函数
* @params option -> 可选参数
* @return effect -> 执行watchEffect
*/
let effect = (fn, options = {}) => {
// 包裹一次effect 避免对fn的污染,保证fn纯净
let effect = (...args) => {
try {
active = effect
// 避免了死循环
return fn(...args)
} finally {
active = null
}
} // 更新数据时也需要让schedular执行
effect.options = options
// 用于反向查找
effect.deps = []; return effect
} let cleanUpEffect = (effect) => {
const { deps } = effect;
deps.forEach(dep => dep.delete(effect))
} let watchEffect = function (cb) {
let runner = effect(cb)
runner()
// 返回一个stop函数,清楚当前的监听
return () => {
cleanUpEffect(runner)
}
}
// 需要有个队列来存储各项任务
let queue = []
// 通过微任务方式去执行队列中的任务
let nextTick = (cb) => Promise.resolve().then(cb)
// 将任务添加到队列
let queueJob = (job) => {
if (!queue.includes(job)) {
queue.push(job)
nextTick(flushJobs)
}
} // 执行队列中的任务
let flushJobs = () => {
let job
while ((job = queue.shift()) !== undefined) {
job()
}
} // 收集更多依赖
class Dep {
// 依赖收集,将响应依赖添加到deps中
constructor() {
this.deps = new Set()
} depend() {
if (active) {
this.deps.add(active)
// 添加依赖时追加当前的deps, 实现双向互通。双向索引
active.deps.push(this.deps)
}
}
// 通知所有依赖更新
notify() {
// 将任务加到队列中
this.deps.forEach((dep) => {
dep.options && dep.options.schedular && dep.options.schedular()
queueJob(dep)
})
}
} let ref = (initValue) => {
let value = initValue
let dep = new Dep() return Object.defineProperty({}, 'value', {
get() {
dep.depend()
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
} let count = ref(1) function add() {
document.getElementById('add').addEventListener('click', () => {
count.value++
})
} add() let stop = watchEffect(() => {
document.getElementById('text').innerText = count.value
}) setTimeout(() => {
stop();
}, 3000); })()

免责声明

本文是通过对vue响应式computed计算属性,watch, watchEffect源码学习的一些笔记分享,会涉及到一些引用,出处不详,如商业用途谨慎转载。

最新文章

  1. iOS 封装添加按钮的方法
  2. jmeter(三)Sample之SOAP/XML-RPC Request
  3. 被swoole坑哭的PHP程序员
  4. placeholder 使用
  5. mormot THttpApiServer使用例子
  6. IOS实现中间凸起圆形TabBar
  7. C 几种异常机制简单讲述
  8. textarea中的文字自动换行问题
  9. sql语句实现累计数
  10. java 生产者 与 消费者的案例
  11. 有返回值的函数,如果包含try/catch块,经常会出现没有return的错误
  12. 一道风骚的DP
  13. HTML5-2
  14. 海龟绘图turtle库之二级基础编程题
  15. WPF GridView的列宽度设置为按比例分配
  16. 8月10日CSS总结
  17. 解决Linux关闭SSH,终端后运行程序终止问题(包括后台)
  18. ANSI和UNICODE编程的注意事项
  19. 基于SVM.NET的验证码识别算法实现
  20. 快速清除SQL2008日志文件

热门文章

  1. Linux基础命令---ab测试apache性能
  2. SVN的基本介绍\服务器配置
  3. 注册页面的servlet
  4. 使用递归方法,遍历输出以.java结尾的文件
  5. 回溯——51. N皇后
  6. 网络协议之:基于UDP的高速数据传输协议UDT
  7. IT过来人的10点经验谈
  8. Kerberos认证
  9. Wireshark(五):TCP窗口与拥塞处理
  10. [BUUCTF]REVERSE——rsa