# Ссылки на элементы шаблона

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

Подразумевается, что уже изучили и разобрались с разделами Введение в Composition API и Основы реактивности. Если нет — прочитайте их сначала.

При использовании Composition API, концепции реактивных ссылок и ссылок на элементы шаблона унифицированы. Чтобы получить ссылку на элемент в шаблоне или экземпляр компонента, необходимо объявить ref-ссылку как обычно и затем вернуть её из setup():

<template>
  <div ref="root">Это корневой элемент</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // элемент DOM будет определён в ref после первоначальной отрисовки
        console.log(root.value) // <div>Это корневой элемент</div>
      })

      return {
        root
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

В этом примере предоставляется доступ к root в контексте отрисовки и выполняется его привязка к блоку в качестве ссылки на него через ref="root". В алгоритме обновления виртуального DOM, если ключ ref у VNode соответствует ссылке в контексте отрисовки, то соответствующему элементу или компоненту VNode будет присвоено значение этой ссылки. Это выполняется в процессе монтирования / обновления виртуального DOM, поэтому ссылки на элементы шаблона будут доступны только после первоначальной отрисовки.

Ссылки, используемые как ссылки на элементы шаблона, ведут себя точно также, как и любые другие ref-ссылки: они реактивны и могут быть переданы в функции композиции (или возвращены из них).

# Использование с JSX

export default {
  setup() {
    const root = ref(null)

    return () =>
      h('div', {
        ref: root
      })

    // с использованием JSX
    return () => <div ref={root} />
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Использование внутри v-for

В Composition API ссылки на элементы шаблона не имеют специальной обработки если используются внутри v-for. Поэтому следует использовать функции для выполнения пользовательской обработки:

<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // убедитесь, что сбрасываете ссылки перед каждым обновлением
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>
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

# Отслеживание ссылок на элементы шаблона

Отслеживание изменений ссылок на элементы шаблона может быть альтернативой использованию хуков жизненного цикла, как было показано в предыдущем примере.

Главное отличие от хуков жизненного цикла в том, что эффекты watch() и watchEffect() будут запускаться перед монтированием или обновлением DOM, поэтому ссылка на элемент шаблона не будет обновлена при запуске эффекта наблюдателем:












 
 
 
 
 








<template>
  <div ref="root">Это корневой элемент</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        // Этот эффект будет запущен перед обновлением DOM и, следовательно,
        // ссылка на элемент шаблона ещё не содержит ссылки на элемент.
        console.log(root.value) // => null
      })

      return {
        root
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Поэтому методы наблюдателей, в которых используются ссылки на элементы шаблона, должны объявляться с опцией flush: 'post'. В таком случае эффект будет запускаться после обновления DOM и гарантировать, что ссылки на элементы шаблона останутся синхронизированными с DOM и ссылаются на правильный элемент.












 
 
 
 
 
 








<template>
  <div ref="root">Это корневой элемент</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div>Это корневой элемент</div>
      },
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24