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>

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

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

    Когда мы используем customRef, нам следует быть осторожными с возвращаемым значением его геттера, особенно когда при каждом вызове геттера генерируется новый объектный тип данных. Это влияет на отношения между родительским и дочерним компонентами, где такой customRef был передан в качестве пропса.

    Рендер-функция родительского компонента может быть запущена из-за изменений в другом реактивном состоянии. Во время повторного рендеринга значение нашего customRef переоценивается, возвращая новый объектный тип данных в качестве пропса дочернему компоненту. Этот пропс сравнивается с его предыдущим значением в дочернем компоненте, и поскольку они разные, реактивные зависимости customRef запускаются в дочернем компоненте. В это время реактивные зависимости в родительском компоненте не запускаются, потому что геттер customRef не был вызван, и его зависимости не были запущены в результате.

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

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 также вызывается в области действия эффекта.

Если эта функция будет вызвана без активной области действия эффекта, будет выведено предупреждение. В версии 3.5+ это предупреждение можно отключить, передав true в качестве второго аргумента.

  • Тип

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