# Реактивные 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

Если в объект присваивается значение реактивной 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

Deployed on Netlify.
Последнее обновление: 2021-03-02, 13:34:56 UTC