Skip to content

Реактивность: Продвинутая

shallowRef()

Неглубокая реализация ref().

triggerRef()

Принудительный запуск эффектов, зависящих от неглубокого ref-объекта. Обычно это используется после глубоких изменений внутреннего значения неглубокого ref-объекта.

  • Тип

    ts
    function triggerRef(ref: ShallowRef): void
  • Пример

    js
    const shallow = shallowRef({
      greet: 'Привет, мир'
    })
    
    // Выведет в консоль "Привет, мир" один раз при первом проходе
    watchEffect(() => {
      console.log(shallow.value.greet)
    })
    
    // Это не вызовет эффекта, поскольку ref-объект неглубокий
    shallow.value.greet = 'Привет, вселенная'
    
    // Выведет "Привет, вселенная"
    triggerRef(shallow)

customRef()

Создаёт пользовательский ref-объект с возможностью явно контролировать отслеживание зависимостей и управлять вызовом обновлений.

  • Тип

    ts
    function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
    
    type CustomRefFactory<T> = (
      track: () => void,
      trigger: () => void
    ) => {
      get: () => T
      set: (value: T) => void
    }
  • Подробности

    customRef() ожидает функцию-фабрику, которая получает в качестве аргументов функции track и trigger и должна возвращать объект с методами get и set.

    В общем случае track() следует вызывать внутри get(), а trigger() - внутри set(). Однако вы имеете полный контроль над тем, когда их следует вызывать и следует ли вызывать вообще.

  • Пример

    Создание debounce 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
    <script setup>
    import { useDebouncedRef } from './debouncedRef'
    const text = useDebouncedRef('hello')
    </script>
    
    <template>
      <input v-model="text" />
    </template>

    Попробовать в песочнице

shallowReactive()

Неглубокая реализация reactive().

  • Тип

    ts
    function shallowReactive<T extends object>(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<T extends object>(target: T): Readonly<T>
  • Подробности

    В отличие от 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<T>(proxy: T): T
  • Подробности

    toRaw() может возвращать исходный объект из прокси, созданных с помощью reactive(), readonly(), shallowReactive() или shallowReadonly().

    Применяется в крайнем случае, когда требуется только чтение без доступа/отслеживания или запись без инициирования изменений. Не рекомендуется сохранять постоянную ссылку на оригинальный объект. Используйте с осторожностью.

  • Пример

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

markRaw()

Помечает объект таким образом, что он никогда не будет преобразован в прокси. Возвращает сам объект.

  • Тип

    ts
    function markRaw<T extends object>(value: T): T
  • Пример

    js
    const foo = markRaw({})
    console.log(isReactive(reactive(foo))) // false
    
    // также работает при вложении внутрь других реактивных объектов
    const bar = reactive({ foo })
    console.log(isReactive(bar.foo)) // false

    Используйте с осторожностью

    markRaw() и неглубокое API, такие как shallowReactive(), позволяют выборочно отказаться от стандартного глубокого преобразования reactive/readonly и внедрить в граф состояний необработанные, непроксированные объекты. Они могут использоваться по разным причинам:

    • Некоторые значение не должны быть реактивными. Например, сторонний комплексный экземпляр класса или объект компонента Vue.

    • Пропуск преобразования в прокси может улучшить производительность при отрисовке больших списков с иммутабельными (неизменяемыми) данными.

    Пропуск преобразования — продвинутая техника, потому что опциональное отключение доступно только на корневом уровне. Если установить вложенный неотмеченный исходный объект в реактивный объект и получить к нему доступ, то вернётся его проксированная версия. Это может привести к опасности идентификации, то есть к выполнению операции, которая основывается на идентификации объекта, но использует как исходную, так и проксированную версию одного и того же объекта:

    js
    const foo = markRaw({
      nested: {}
    })
    
    const bar = reactive({
      // хотя `foo` отмечен как raw, foo.nested не будет таким.
      nested: foo.nested
    })
    
    console.log(foo.nested === bar.nested) // false

    С опасностью идентификации сталкиваются редко. Однако, чтобы правильно использовать эти API, избегая опасности идентификации, необходимо хорошо понимать принцип работы системы реактивности.

effectScope()

Создаёт объект области действия эффекта, который может захватывать другие реактивные эффекты (например, вычисляемые свойства и наблюдатели), созданные внутри него, чтобы иметь возможность уничтожить все эти эффекты вместе. Подробные примеры использования этого API приведены в соответствующем RFC.

  • Тип

    ts
    function effectScope(detached?: boolean): EffectScope
    
    interface EffectScope {
      run<T>(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))
    })
    
    // для уничтожения всех эффектов в области действия
    scope.stop()

getCurrentScope()

Возвращает текущую активную область действия эффекта, если таковая есть.

  • Тип

    ts
    function getCurrentScope(): EffectScope | undefined

onScopeDispose()

Регистрация коллбэка для текущей активной области действия эффекта. Коллбэк будет вызван, когда связанная с ним область действия эффекта будет остановлена.

Этот метод можно использовать как не связанную с компонентами замену onUnmounted в переиспользуемых функциях композиций, поскольку функция setup() каждого компонента Vue также вызывается в области действия эффекта.

  • Тип

    ts
    function onScopeDispose(fn: () => void): void
Реактивность: ПродвинутаяУже загружено