# Реактивные ref-ссылки

В этом разделе в примерах кода используется синтаксис однофайловых компонентов

# ref

Получает внутреннее значение и возвращает реактивный и мутируемый ref-объект. В этом ref-объекте есть только одно свойство .value, которое указывает на внутреннее значение.

Пример:

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

count.value++
console.log(count.value) // 1
1
2
3
4
5

Если свойству value ref-ссылки передаётся объект, то объект становится глубоко реактивным с помощью функции reactive.

Типизация:

interface Ref<T> {
  value: T
}

function ref<T>(value: T): Ref<T>
1
2
3
4
5

Если потребуется определить сложный тип для внутреннего значения ref-ссылки, то это реализуется передачей дженерика аргументом при вызове ref для переопределения вывода типа по умолчанию:

const foo = ref<string | number>('foo') // тип foo: Ref<string | number>

foo.value = 123 // ok!
1
2
3

Когда тип дженерика неизвестен, рекомендуется приводить ref к Ref<T>:

function useState<State extends string>(initial: State) {
  const state = ref(initial) as Ref<State> // state.value -> State extends string
  return state
}
1
2
3
4

# unref

Возвращает внутреннее значение, если аргумент является ref, в противном случае — сам аргумент. Это всего лишь синтаксический сахар для val = isRef(val) ? val.value : val.

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // значение unwrapped гарантированно будет числом
}
1
2
3

# toRef

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

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3
1
2
3
4
5
6
7
8
9
10
11
12

toRef пригодится при передаче ref-ссылки из входного параметра в функцию композиции:

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}
1
2
3
4
5

toRef возвращает ref-ссылку пригодную для использования, даже если на данный момент свойства в источнике не существует. Это особенно полезно при работе с необязательными входными параметрами, которые не будут подхвачены toRefs.

# toRefs

Преобразует реактивный объект в обычный объект, в котором каждое свойство будет ref, указывающей на соответствующее свойство исходного объекта.

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
  Тип stateAsRefs будет:

  {
    foo: Ref<number>,
    bar: Ref<number>
  }
*/

// Реактивная ref-ссылка и оригинальное свойство «связаны»
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

toRefs полезен при возвращении реактивного объекта из функции композиции, чтобы в компоненте использовать деструктуризацию/оператор разложения к возвращаемому объекту без потери реактивности:

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // логика, работающая с состоянием

  // преобразуем к refs при возвращении результата
  return toRefs(state)
}

export default {
  setup() {
    // теперь можно использовать деструктуризацию без потери реактивности
    const { foo, bar } = useFeatureX()

    return {
      foo,
      bar
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

toRefs будет генерировать ref-ссылки только для тех свойств, которые были в исходном объекте. Для создания ref-ссылки для конкретного свойства следует использовать toRef.

# isRef

Проверяет является ли значение ref-объектом.

# customRef

Создаёт пользовательскую ref-ссылку с возможностью явно контролировать отслеживание зависимостей и управлять вызовом обновлений. Ожидает функцию-фабрику, получающую аргументами функции track и trigger, и возвращающую объект с get и set.

Пример пользовательской ref-ссылки для реализации debounce вместе с v-model:

<input v-model="text" />
1
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)
      }
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello')
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Типизация:

function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
  track: () => void,
  trigger: () => void
) => {
  get: () => T
  set: (value: T) => void
}
1
2
3
4
5
6
7
8
9

# shallowRef

Создание ref-ссылки, которая отслеживает изменение своего .value, но не делает это значение реактивным.

const foo = shallowRef({})
// изменение значения ref-ссылки реактивно
foo.value = {}
// но значение НЕ ПРЕОБРАЗУЕТСЯ в реактивное.
isReactive(foo.value) // false
1
2
3
4
5

См. также: Создание автономных ссылок на реактивные значения

# triggerRef

Выполняет любые эффекты, привязанные вручную к shallowRef.

const shallow = shallowRef({
  greet: 'Привет, мир'
})

// Выведет "Привет, мир" один раз при первом проходе
watchEffect(() => {
  console.log(shallow.value.greet)
})

// Это не вызовет эффект, потому что ref-ссылка неглубокая
shallow.value.greet = 'Привет, вселенная'

// Выведет "Привет, вселенная"
triggerRef(shallow)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

См. также: Вычисляемые свойства и методы-наблюдатели — watchEffect