来说说自己的理解
-
这几个 api 真挺难分清楚的 刚开始学的时候迷迷糊糊 区别以及应用场景 都感觉答不上来
-
所以也挺多时间进行一个梳理和区分 源码看了一些 就目前自己的了解做一个记录吧
-
我觉得要理解的话 可以从这几个点出发
-
为什么会设计出这个 属性?他是基于什么原因设计的?用于解决什么问题?
-
这几个 api 实现思路是怎样的?(读源码了)
-
他们之间的具体区别?
- 知道区别就能分析出应用场景了 什么时候该使用哪个
-
使用的时候一些坑 一些细节?
-
computed
-
例子
-
<div id="computed-basics"> <p>Has published books:</p> <span>{{ publishedBooksMessage }}</span> </div> data() { return { author: { name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] } } }, computed: { // 计算属性的 getter publishedBooksMessage() { // `this` 指向 vm 实例 return this.author.books.length > 0 ? 'Yes' : 'No' } }
-
-
首先计算属性是基于方法来升级的 上面的数据变动 完全可以使用一个函数来实现
-
但是我们希望得到的效果是 当数据发生变化时 与其相关联的数据自动发生改变
-
也就是说 自动执行相关联的函数 其实响应式的具体实现思想也跟这个类似
- 大概就是 先收集每一个响应式变量所依赖的函数(这些函数也被称为 副作用函数) 当这个变量发生改变是 与这个变量相关联的函数会全部重新执行一次 那么其他使用到这个变量的数据也会响应改变了
-
-
另一个就是 简化模板中的复杂表达式
-
{{ firstName + lastName }} {{ fullName }} computed: { fullName: { get() { return this.firstName + this.lastName } } }
-
-
官网提到 计算属性将基于变量的响应依赖关系缓存 计算属性只会在相关响应式依赖发生改变时重新求值 这就意味着只要
author.books还没有发生改变 多次访问publishedBookMessage时计算属性会立即返回之前的计算结果 而不必再次执行函数-
相比之下,每当触发重新渲染时,调用方法将始终会再次执行函数
- 渲染和刷新还是有区别的 渲染是数据更新 刷新是整个页面
-
-
那么怎么理解他的缓存性呢
-
这里我觉得还是要读一下源码比较好理解 大概说一下为什么具有缓存行吧 我刚开始对这一块的理解听迷糊的
-
首先 我们知道 当他发现数据变化时 就会重新计算并且返回 而缓存性就在于 当我们一二次第三次访问 这个属性的时候 它不需要重新计算
- 比如 上面 this.author.books 数据改变了 那么会重新计算 但是有很多位置引入了这个 this.author.books 第一个位置读取的时候 计算返回了 那么第一个位置 或者第二次读取的时候 就不会再重新计算了 而是直接返回之前的计算结果 源码主要是通过 dirty: Boolean 来判断是否要重新计算
-
-
写法
-
const count = ref(1) const plusOne = computed(() => count.value + 1) const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } })
-
watch
-
computed 只是为了获取一个返回值 但是有时数据改变时 我们需要执行一些操作 那么就有了 watch
-
官网中提到 Vue 通过
watch选项提供了一个更通用的方法来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的 -
例子的话看官网的就行了
- 大概就是数据发生变化 就发送一个 ajax 请求重新获取数据
-
写法
-
// 侦听一个 getter const state = reactive({ count: 0 }) watch( // 这个箭头函数 就是简化的 get () => state.count, (count, prevCount) => { /* ... */ } ) // 直接侦听一个 ref const count = ref(0) watch(count, (count, prevCount) => { /* ... */ }) // 侦听多个源 watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
-
watcheffect
-
我觉得 主要是对 watch 进行一些补充和升级吧 vue3 中出现的新的 响应式 api
-
说说区别 只知道进行了哪些补充了
-
watchEffect不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而watch只能监听指定的属性而做出变更(v3开始可以同时指定多个) -
watch 可以获取到新值与旧值(更新前的值),而
watchEffect是拿不到的- 这个怎么感觉更拉了啊
-
watchEffect 如果存在的话 在组件初始化的时候就会执行一次用以收集依赖(与
computed同理)而后收集到的依赖发生变化 这个回调才会再次执行 而 watch 不需要 因为他一开始就指定了依赖
-
-
写法
-
const count = ref(0) watchEffect(() => console.log(count.value))
-
停止侦听
-
watchEffect 会返回一个用于停止这个监听的函数,如法如下: const stop = watchEffect(() => { /* ... */ }) // later stop()
清除副作用
-
有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个
onInvalidate函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:- 副作用即将重新执行时
- 侦听器被停止 (如果在
setup()或生命周期钩子函数中使用了watchEffect,则在组件卸载时)
watchEffect(onInvalidate => { const token = performAsyncOperation(id.value) onInvalidate(() => { // id has changed or watcher is stopped. // invalidate previously pending async operation token.cancel() }) })
总结
-
computed
-
解决自动更新数据的问题 替代方法
- 主要区别是具有缓存性 依赖不变时不会重新计算
-
简化模板中的复杂表达式
-
默认的是 get 函数 返回一个只读的响应式对象 也可以自定义添加一个 set 方法
-
最重要的是 关注派生出来的返回值
- 简单地说 只要数据变化 我就会得到一个新的返回值去使用
-
-
watch
-
传入一个 get 函数 之后执行一个回到函数
-
最重要的是 不关注返回值 而关注于侦听数据变化 执行回调函数
- 简单地说就是 只要数据变化 我想做一些其他的事情(回调函数)
-
watch 是惰性执行 也就是只有监听的值发生变化的时候才会执行 但是watchEffect不同,每次代码加载watchEffect都会执行
- 第一次不执行 但是可以通过属性控制 immediate
-
watch 只能监听响应式数据:ref 定义的属性和 reactive 定义的对象,如果直接监听 reactive 定义对象中的属性是不允许的,除非使用函数转换一下
-
-
watchEffect
-
watchEffect 如果监听 reactive 定义的对象是不起作用的 只能监听对象中的属性
-
watchEffect不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而watch只能监听指定的属性而做出变更(v3开始可以同时指定多个) -
watch 可以获取到新值与旧值(更新前的值),而
watchEffect是拿不到的- 这个怎么感觉更拉了啊
-
watchEffect 如果存在的话 在组件初始化的时候就会执行一次用以收集依赖(与
computed同理)而后收集到的依赖发生变化 这个回调才会再次执行 而 watch 不需要 因为他一开始就指定了依赖 -
还有一些进阶内容
watch与watchEffect在手动停止侦听、清除副作用 (将onInvalidate作为第三个参数传递给回调)、刷新时机和调试方面有相同的行为
-