一、响应性原理
数据模型 是被代理的 JavaScript 对象。而当你修改它们时,视图会进行更新。这让状态管理非常简单直观。
理解其工作原理同样重要,这样可以避开一些常见的问题。
1 什么是响应性?
响应性是 一种允许我们以声明式的方式去适应变化的一种编程范例。如excell。
js如何实现响应性
- 检测其中某一个值是否发生变化
- 用跟踪 (track) 函数修改值
- 用触发 (trigger) 函数更新为最新的值
2 Vue如何追踪变化?
底层原理:
当把一个普通的 JavaScript 对象作为 data
选项传给应用或组件实例的时候,Vue 会使用带有 getter 和 setter 的处理程序遍历其所有 property 并将其转换为 Proxy
这是 ES6 仅有的特性,但是我们在 Vue 3 版本也使用了 Object.defineProperty
来支持 IE 浏览器。
两者具有相同的 Surface API,但是 Proxy 版本更精简,同时提升了性能。
Proxy相关:
Proxy 是一个 包含另一个对象或函数 并允许你对其进行拦截 的对象。
用法:new Proxy ( tatget,handler )
a. 我们把对象包装在 Proxy 里的同时,可以对其进行拦截。这种拦截被称为陷阱。
b. Proxy 还提供了另一个特性。我们不必像这样返回值:target[prop]
,而是可以进一步使用一个名为 Reflect
的方法,它允许我们正确地执行 this
绑定。
详见: https://vue3js.cn/docs/zh/guide/reactivity.html
总结:
将对象作为数据传递给组件实例时,Vue 会将其转换为 Proxy。
这个 Proxy 使 Vue 能够在 property 被访问或修改时 执行依赖项跟踪 和 更改通知。每个 property 都被视为一个依赖项。
首次渲染后,组件将跟踪一组依赖列表——即在渲染过程中被访问的 property。
反过来,组件就成为了其每个 property 的订阅者。当 Proxy 拦截到 set 操作时,该 property 将通知其所有订阅的组件重新渲染。
响应性基础
1 reactive 声明响应式状态(为 JavaScript 对象创建响应式状态)
import { reactive } from 'vue' // 响应式状态 const state = reactive({ count: 0 })
2ref 创建独立的响应式值(比如一个字符串)
ref
会返回一个可变的响应式对象,该对象作为它的内部值——一个响应式的引用,这就是名称的来源。
此对象只包含一个名为 value
的 property 。
import { ref } from 'vue' const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1
⚠️ ref展开:当 ref 作为渲染上下文 (从 setup() 中返回的对象) 上的 property 返回 并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加 .value
a. 当 ref
作为响应式对象的 property 被访问或更改时,为使其行为类似于普通 property,它会自动展开内部值。
b. 如果将新的 ref 赋值给 现有 ref 的 property,新的 ref 将会替换旧的 ref.
c. ref 展开仅发生在被响应式 Object
嵌套的时候。当从 Array
或原生集合类型如 Map
访问 ref 时,不会进行展开.
3toRefs :
响应式状态 解构,toRefs 可 保留 与源对象的响应式关联
import { reactive, toRefs } from 'vue' const book = reactive({ author: 'Vue Team', year: '2020', title: 'Vue 3 Guide', description: 'You are reading this book right now ;)', price: 'free' }) let { author, title } = toRefs(book) title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref console.log(book.title) // 'Vue 3 Detailed Guide'
4readonly
:使用readonly 防止更改响应式对象 (可以基于原始对象 创建一个只读的 Proxy 对象)
import { reactive, readonly } from 'vue' const original = reactive({ count: 0 }) const copy = readonly(original) // 在copy上转换original 会触发侦听器依赖 original.count++ copy.count++ // 转换copy 将导失败并导致警告 "Set operation on key 'count' failed: target is readonly."
响应式计算和侦听
1computed : 计算值(当需要依赖其他状态时,可用computed来计算得出。它接受 getter 函数并为 getter 返回的值返回一个不可变的响应式 ref 对象。)
const count = ref(1) const plusOne = computed(() => count.value++) console.log(plusOne.value) // 2 plusOne.value++ // error
const count = ref(1) const plusOne = computed({ //或者,它可以使用一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。 get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0
2watchEffect: 副作用(根据响应式状态 自动应用 和 重新应用 副作用。它立即执行 传入的 一个函数,同时响应式追踪依赖,并在该依赖变更时,重新运行该函数。)
const count = ref(0) watchEffect(() => console.log(count.value)) // -> logs 0 setTimeout(() => { count.value++ // -> logs 1 }, 100)
stop: 停止侦听(当 watchEffect
在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止)
const stop = watchEffect(() => { /* ... */ })
// later stop()
onInvalidate: 清除副作用(侦听副作用传入的函数 可以接收一个 onInvalidate
函数作入参,用来注册清理失效时的回调)
3watch: 等同于组件侦听器 (侦听特定的数据源,并在回调函数中执行副作用。默认情况下,它也是惰性的,即只有当被侦听的源发生变化时才执行回调)
与 watchEffect 比较,watch
允许我们:
- 懒执行 副作用;
- 更具体地说明什么状态应该触发侦听器重新运行;
- 访问 侦听状态变化前后的值