百科狗-知识改变命运!
--

响应式 API:进阶 ​

是丫丫呀2年前 (2023-11-21)阅读数 36#技术干货
文章标签对象

响应式 API:进阶 ​

shallowRef() ​

ref()的浅层作用形式。

  • 类型

    ts
    function shallowRef(value: T): ShallowRef
    
    interface ShallowRef {
      value: T
    }
  • 详细信息

    ref()不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对.value的访问是响应式的。

    shallowRef()常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。

  • 示例

    js
    const state = shallowRef({ count: 1 })
    
    // 不会触发更改
    state.value.count = 2
    
    // 会触发更改
    state.value = { count: 2 }
  • 参考

    • 指南 - 减少大型不可变结构的响应性开销
    • 指南 - 与其他状态系统集成

triggerRef() ​

强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。

  • 类型

    ts
    function triggerRef(ref: ShallowRef): void
  • 示例

    js
    const shallow = shallowRef({
      greet: 'Hello, world'
    })
    
    // 触发该副作用第一次应该会打印 "Hello, world"
    watchEffect(() => {
      console.log(shallow.value.greet)
    })
    
    // 这次变更不应触发副作用,因为这个 ref 是浅层的
    shallow.value.greet = 'Hello, universe'
    
    // 打印 "Hello, universe"
    triggerRef(shallow)

customRef() ​

创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。

  • 类型

    ts
    function customRef(factory: CustomRefFactory): Ref
    
    type CustomRefFactory = (
      track: () => void,
      trigger: () => void
    ) => {
      get: () => T
      set: (value: T) => void
    }
  • 详细信息

    customRef()预期接收一个工厂函数作为参数,这个工厂函数接受tracktrigger两个函数作为参数,并返回一个带有getset方法的对象。

    一般来说,track()应该在get()方法中调用,而trigger()应该在set()中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。

  • 示例

    创建一个防抖 ref,即只在最近一次 set 调用后的一段固定间隔后再调用:

    js
    import { customRef } from 'vue'
    
    export function useDebouncedRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track()
            return value
          },
          set(newValue) {
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()
            }, delay)
          }
        }
      })
    }

    在组件中使用:

    vue
    
    import { useDebouncedRef } from './debouncedRef'
    const text = useDebouncedRef('hello')
    
    
    
      
    

    在演练场中尝试一下

shallowReactive() ​

reactive()的浅层作用形式。

  • 类型

    ts
    function shallowReactive(target: T): T
  • 详细信息

    reactive()不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。

    谨慎使用

    浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。

  • 示例

    js
    const state = shallowReactive({
      foo: 1,
      nested: {
        bar: 2
      }
    })
    
    // 更改状态自身的属性是响应式的
    state.foo++
    
    // ...但下层嵌套对象不会被转为响应式
    isReactive(state.nested) // false
    
    // 不是响应式的
    state.nested.bar++

shallowReadonly() ​

readonly()的浅层作用形式

  • 类型

    ts
    function shallowReadonly(target: T): Readonly
  • 详细信息

    readonly()不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。

    谨慎使用

    浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。

  • 示例

    js
    const state = shallowReadonly({
      foo: 1,
      nested: {
        bar: 2
      }
    })
    
    // 更改状态自身的属性会失败
    state.foo++
    
    // ...但可以更改下层嵌套对象
    isReadonly(state.nested) // false
    
    // 这是可以通过的
    state.nested.bar++

toRaw() ​

根据一个 Vue 创建的代理返回其原始对象。

  • 类型

    ts
    function toRaw(proxy: T): T
  • 详细信息

    响应式 API:进阶 ​

    toRaw()可以返回由reactive()readonly()shallowReactive()或者shallowReadonly()创建的代理对应的原始对象。

    这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。

  • 示例

    js
    const foo = {}
    const reactiveFoo = reactive(foo)
    
    console.log(toRaw(reactiveFoo) === foo) // true

markRaw() ​

将一个对象标记为不可被转为代理。返回该对象本身。

  • 类型

    ts
    function markRaw(value: T): T
  • 示例

    js
    const foo = markRaw({})
    console.log(isReactive(reactive(foo))) // false
    
    // 也适用于嵌套在其他响应性对象
    const bar = reactive({ foo })
    console.log(isReactive(bar.foo)) // false

    谨慎使用

    markRaw()和类似shallowReactive()这样的浅层式 API 使你可以有选择地避开默认的深度响应/只读转换,并在状态关系谱中嵌入原始的、非代理的对象。它们可能出于各种各样的原因被使用:

    • 有些值不应该是响应式的,例如复杂的第三方类实例或 Vue 组件对象。

    • 当呈现带有不可变数据源的大型列表时,跳过代理转换可以提高性能。

    这应该是一种进阶需求,因为只在根层访问能到原始值,所以如果把一个嵌套的、没有标记的原始对象设置成一个响应式对象,然后再次访问它,你获取到的是代理的版本。这可能会导致对象身份风险,即执行一个依赖于对象身份的操作,但却同时使用了同一对象的原始版本和代理版本:

    js
    const foo = markRaw({
      nested: {}
    })
    
    const bar = reactive({
      // 尽管 `foo` 被标记为了原始对象,但 foo.nested 却没有
      nested: foo.nested
    })
    
    console.log(foo.nested === bar.nested) // false

    识别风险一般是很罕见的。然而,要正确使用这些 API,同时安全地避免这样的风险,需要你对响应性系统的工作方式有充分的了解。

effectScope() ​

创建一个 effect 作用域,可以捕获其中所创建的响应式副作用 (即计算属性和侦听器),这样捕获到的副作用可以一起处理。对于该 API 的使用细节,请查阅对应的 RFC。

  • 类型

    ts
    function effectScope(detached?: boolean): EffectScope
    
    interface EffectScope {
      run(fn: () => T): T | undefined // 如果作用域不活跃就为 undefined
      stop(): void
    }
  • 示例

    js
    const scope = effectScope()
    
    scope.run(() => {
      const doubled = computed(() => counter.value * 2)
    
      watch(doubled, () => console.log(doubled.value))
    
      watchEffect(() => console.log('Count: ', doubled.value))
    })
    
    // 处理掉当前作用域内的所有 effect
    scope.stop()

getCurrentScope() ​

如果有的话,返回当前活跃的 effect 作用域。

  • 类型

    ts
    function getCurrentScope(): EffectScope | undefined

onScopeDispose() ​

在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域停止时会调用这个回调函数。

这个方法可以作为可复用的组合式函数中onUnmounted的替代品,它并不与组件耦合,因为每一个 Vue 组件的setup()函数也是在一个 effect 作用域中调用的。

  • 类型

    ts
    function onScopeDispose(fn: () => void): void

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)
最新文章