vue入门到精通(五)
创始人
2024-05-12 05:01:32

3、响应式核心

3.1ref()

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

ref 对象是可更改的,也就是说你可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪,并且写操作会触发与之相关的副作用。

如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。

将一个 ref 赋值给为一个 reactive 属性时,该 ref 会被自动解包

const count = ref(0)
console.log(count.value) // 0count.value++
console.log(count.value) // 1

18_composition/80_composition_ref.html



ref

count: {{ count }}
state.count: {{ state.count }}

以后创建 非 对象类型的数据 使用 ref, 创建对象类型的数据建议使用 reactive

3.2computed ()

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

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

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

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

const count = ref(1)
const plusOne = computed({get: () => count.value + 1,set: (val) => {count.value = val - 1}
})plusOne.value = 1
console.log(count.value) // 0

18_composition/81_composition_computed.html



计算属性

count: {{ count }} - {{ doubleCount }} - {{ plusOne }}

3.3 reactive()

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

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。

创建一个响应式对象:

const obj = reactive({ count: 0 })
obj.count++

ref 的解包:

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 的解包:

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

将一个 ref 赋值给为一个 reactive 属性时,该 ref 会被自动解包:(讲解ref时已经说明)

const count = ref(1)
const obj = reactive({})obj.count = countconsole.log(obj.count) // 1
console.log(obj.count === count.value) // true

18_composition/82_composition_reactive.html



reactive

state.count: {{ state.count }}

初始值 对象 reactive 其余用ref

3.4 readonly()

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

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

const original = reactive({ count: 0 })const copy = readonly(original)watchEffect(() => {// 用来做响应性追踪console.log(copy.count)
})// 更改源属性会触发其依赖的侦听器
original.count++// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning

18_composition/83_composition_readonly.html



readonly

count: {{ count }}
copy: {{ copy }}

3.5 watchEffect()

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

第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求。

返回值是一个用来停止该副作用的函数。

const count = ref(0)watchEffect(() => console.log(count.value))
// -> 输出 0count.value++
// -> 输出 1

副作用清除:

watchEffect((onInvalidate) => {console.log(id.value) const timer = setTimeout(() => {console.log('请求成功') // 2秒之内点击列表 只显示一次data.value = '数据' + id.value}, 2000)onInvalidate(() => {clearTimeout(timer)})
})

停止侦听器:

 const stop = watchEffect(() => {console.log(count.value)})// 当不再需要此侦听器时:const stopWatch = () => {stop()}

18_composition/84_composition_watchEffect.html



watchEffect

  • 请求第一条数据
  • 请求第二条数据
  • 请求第三条数据

watchEffect没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行

3.6 watch()

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

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

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

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

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

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

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

    • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
    • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。

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

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

    侦听一个 getter 函数:

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

    侦听一个 ref:

    const count = ref(0)
    watch(count, (count, prevCount) => {/* ... */
    })
    

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

    watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {/* ... */
    })
    

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

    const state = reactive({ count: 0 })
    watch(() => state,(newValue, oldValue) => {// newValue === oldValue},{ deep: true }
    )
    

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

    const state = reactive({ count: 0 })
    watch(state, () => {/* 深层级变更状态所触发的回调 */
    })
    

18_composition/85_composition_watch.html



85_composition_watch

{{ state.obj.a }}

4、工具函数 -了解

4.1 toRef()

基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。

18_composition/86_composition_toRef.html



toRef

4.2 toRefs()

将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

18_composition/87_composition_toRefs.html



toRefs

toRefs 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef

相关内容

热门资讯

六十年一遇“火马旺运年”!本命... (来源:大公馆)一件比三件暖和,100%澳大利亚超细美丽诺羊毛,极为柔软顺滑还不会变形,也更加亲肤,...
周黑鸭×米村拌饭官宣联名 咕嘟... 来源:@证券市场红周刊微博1月10日,黑鸭风味开创者周黑鸭与中式快餐品牌米村拌饭,正式官宣联名合作,...
俄驻委内瑞拉大使谴责美对委石油... 转自:北京日报客户端俄罗斯驻委内瑞拉大使梅利克-巴格达萨罗夫9日在加拉加斯说,美国对委内瑞拉的石油禁...
邢宏伟被查,辞任不到1个月 南京晨报2026-01-10 12:05:49据中央纪委国家监委驻全国供销总社纪检监察组、广东省纪委...
“国补”带动年货消费提前火热   本报讯(记者孙杰 孙奇茹)2026年伊始,马年春节也日渐临近,市民开始提前准备年货,不少人趁着元...