Vue3响应式Api:核心(学习笔记)

文档:https://cn.vuejs.org/api/reactivity-core.html

ref()


接受一个内部值,返回一个响应式的、可更改的 ref 对象,此ref对象只有一个指向其内部值的属性 .value

示例
const count = ref(0) // 初始值为0
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

// 如果将一个对象赋值给 ref,那么这个对象将通过reactive转为具有深层次响应式的对象
const refObj = ref({count: 1})
refObj.value.count++
console.log(refObj.value.count) // 2
访问$refs
// template
<form ref="formRef">...</form>

// script setup
const formRef = ref(null) // 变量名formRef 与template中form绑定的ref属性值一致,会自动识别为对应的dom
const handleSubmit = () => {
  formRef.value.validate(async (valid) => {
    if (valid) {
      ...
      formRef.value.resetFields()
    }
  })
}

computed()


接受一个 getter 函数,返回一个只读的响应式ref对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 getset 函数的对象来创建一个可写的 ref 对象。

示例

创建一个只读的计算属性 ref:

let firstName = ref('hello')
let lastName = ref('world')
let fullName = computed(() => firstName.value+' '+lastName.value) // 返回一个响应式ref对象
console.log(fullName.value) // hello world
firstName.value = 'goodbye'
console.log(fullName.value) // goodbye world

const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2

plusOne.value++ // 错误-->只读

创建一个可写的计算属性 ref:

const count = ref(1)
const plusOne = computed({
    get() {
        return count.value
    },
    set(newVal) {
        count.value = newVal
    }
})
plusOne.value = 2
console.log(count.value) // 2
plusOne.value = 3
console.log(count.value) // 3

reactive()


返回一个对象的响应式代理。

详细信息

  • 响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
  • 值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。
示例

创建一个响应式对象:

const reactiveObj = reactive({count: 0})
console.log(reactiveObj.count) // 0
reactiveObj.count++
console.log(reactiveObj.count) // 1

ref解包(不需要.value):

const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

// 会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

注意当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包(需要.value):

const arr = reactive([ref('hello')])
// 这里需要 .value
console.log(arr[0].value)

const map = reactive(new Map([['name', ref('jack')],['sex', ref('man')]]))
// 这里需要 .value
console.log('map', map.get('name').value) // jack

readonly()


接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

示例
const original = reactive({ count: 0 })
const copy = readonly(original)

// 更改源属性会触发其依赖的侦听器
original.count++

// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!

watchEffect()


立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

详细信息

  • 第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
  • 第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
  • 返回值是一个用来停止该副作用的函数。
示例
const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 输出 0

count.value++
// -> 输出 1

副作用清除:

watchEffect(async (onCleanup) => {
  const { response, cancel } = doAsyncWork(id.value)
  // `cancel` 会在 `id` 更改时调用
  // 以便取消之前未完成的请求
  onCleanup(cancel)
  data.value = await response
})

停止侦听器:

const stop = watchEffect(() => {}) // 返回一个停止该副作用的函数
// 当不再需要此侦听器时:
stop()

选项:

watchEffect(() => {}, {
  flush: 'post',
  onTrack(e) {
    debugger
  },
  onTrigger(e) {
    debugger
  }
})

watch()


侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

详细信息
watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的源。
这个来源可以是以下几种:

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • ...或是由以上类型的值组成的数组

第二个参数是在发生变化时要调用的回调函数。
这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。

第三个可选的参数是一个对象
支持以下这些选项:

  • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
  • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器一节。
  • flush:调整回调函数的刷新时机。参考回调的刷新时机一节。
  • onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器一节。

watchEffect() 相比,watch() 使我们可以:

  • 懒执行副作用(watchEffect是立即执行);
  • 更加明确是应该由哪个状态触发侦听器重新执行;
  • 可以访问所侦听状态的前一个值和当前值。
示例

侦听一个 getter 函数:

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 以下是错误的,用上面的方法监听一个对象的属性
watch(state.count, (count, prevCount) => {
    /* ... */
  }
)

当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true } 强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。

const watchGetter = reactive({count: 0})
// 不加deep: true
watch(() => watchGetter, (newVal, oldVal) => {
    console.log("newVal", newVal.count)
    console.log("oldVal", oldVal.count)
})
watchGetter.count = 1 // 此时不会触发回调

// 加deep: true
watch(() => watchGetter, (newVal, oldVal) => {
    // 新值和旧值将是同一个对象
    console.log("newVal", newVal.count) // 1
    console.log("oldVal", oldVal.count) // 1
},{
  deep: true
})
watchGetter.count = 1 // 此时加上deep:true会触发回调

侦听一个 ref:

const watchRef = ref(0)
watch(watchRef, (newVal) => {
    console.log(newVal) // 1
})
watchRef.value = 1

侦听reactive对象:
当直接侦听一个响应式对象时,侦听器会自动启用深层模式。

const watchReactive = reactive({count: {num: 0}})
watch(watchReactive, (newVal) => {
    console.log("newVal", newVal.count.num) // 1
})
watchReactive.count.num = 1

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容