博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Vue 响应性

Posted on 2024-01-28 11:07  linFen  阅读(14)  评论(0编辑  收藏  举报

一、响应性原理

 数据模型 是被代理的 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 允许我们:

  • 懒执行 副作用;
  • 更具体地说明什么状态应该触发侦听器重新运行;
  • 访问 侦听状态变化前后的值