Vue 3.x 响应式:进阶

发布于 2024-09-22 18:03:24 字数 5279 浏览 10 评论 0

shallowReadonly()

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

function shallowReadonly<T extends object>(target: T): Readonly<T>
const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 更改状态自身的属性会失败
state.foo++

// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false

// 这是可以通过的
state.nested.bar++

markRaw()

将一个对象标记为不可被转为响应式。返回该对象本身。

function markRaw<T extends object>(value: T): T
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

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

使用原因

  • 有些值不应该是响应式的,例如复杂的第三方类实例或 Vue 组件对象。
  • 当呈现带有不可变数据源的大型列表时,跳过代理转换可以提高性能。

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

const foo = markRaw({
  nested: {}
})

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

console.log(foo.nested === bar.nested) // false

EffectScope 副作用域

interface EffectScope {
  run<T>(fn: () => T): T | undefined // 如果作用域不活跃就为 undefined
  stop(): void
}
function effectScope(detached?: boolean): EffectScope
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 () 被调用时,它将递归地停止所有 捕获的 effects 和 嵌套的 scopes。
scope.stop()
console.log(scope.run(() => 1)) // 1 run 方法还转发已执行函数的返回值

EffectScope 还能再嵌套

嵌套作用域由其父作用域收集。当父作用域被释放时,它的所有子作用域也将被停止,除非使用 detached 分离参数

const scope = effectScope()

scope.run(() => {
  const doubled = computed(() => counter.value * 2)

  effectScope().run(() => {
    watch(doubled, () => console.log(doubled.value))
  })

  watchEffect(() => console.log('Count: ', doubled.value))
})

// 停用所有 effects, 包括嵌套作用域中的 effects
scope.stop()

使用 detached 分离参数

let nestedScope

const parentScope = effectScope()

parentScope.run(() => {
  const doubled = computed(() => counter.value * 2)

  // 不被父作用域收集和处理
  nestedScope = effectScope(true /* detached */)
  nestedScope.run(() => {
    watch(doubled, () => console.log(doubled.value))
  })

  watchEffect(() => console.log('Count: ', doubled.value))
})

// 停用所有 effects, 但不包括 nestedScope
parentScope.stop()

// 停用 nestedScope
nestedScope.stop()

getCurrentScope()

返回当前活跃的 effect 作用域

function getCurrentScope(): EffectScope | undefined
import { getCurrentScope } from 'vue'

getCurrentScope() // EffectScope | undefined

onScopeDispose()

当相关的 effect 作用域停止时会调用这个回调函数,可以作为可复用的组合式函数中 onUnmounted 的替代品。

它并不与组件耦合,因为每一个 Vue 组件的 setup() 函数也是在一个 effect 作用域中调用的。

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

全局钩子 onScopeDispose () 具有与 onUnmount () 类似的功能,但是它适用于当前作用域而不是组件实例。这有利于可组合函数清除它们的副作用及其范围。由于 setup () 也为组件创建了一个作用域,所以当没有创建显式 effects 作用域时,它将等效于 onUnmount ()

import { onScopeDispose } from 'vue'

const scope = effectScope()

scope.run(() => {
  onScopeDispose(() => {
    console.log('cleaned!')
  })
})

scope.stop() // logs 'cleaned!'

示例

function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function handler(e) {
    x.value = e.x
    y.value = e.y
  }

  window.addEventListener('mousemove', handler)

  //onUnmounted(() => {
  onScopeDispose(() => {
    window.removeEventListener('mousemove', handler)
  })

  return { x, y }
}
export default {
  setup() {
    const enabled = ref(false)
    let mouseState, mouseScope

    const dispose = () => {
      mouseScope && mouseScope.stop()
      mouseState = null
    }

    watch(
      enabled,
      () => {
        if (enabled.value) {
          mouseScope = effectScope()
          mouseState = mouseScope.run(() => useMouse())
        } else {
          dispose()
        }
      },
      { immediate: true }
    )

    onScopeDispose(dispose) // 这仍然有效,因为 Vue 组件现在还在作用域内运行它的 setup(),当组件卸载时将释放这个作用域。
  },
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

送君千里

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

qq_E2Iff7

文章 0 评论 0

Archangel

文章 0 评论 0

freedog

文章 0 评论 0

Hunk

文章 0 评论 0

18819270189

文章 0 评论 0

wenkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文