Олег Марков
3 способа манипулирования DOM на Vue
Введение
Vue — это современный JavaScript-фреймворк, главной задачей которого является упрощение работы с интерфейсом путем связывания данных и DOM. Vue реализует реактивный подход: вы меняете данные, а DOM обновляется автоматически. Однако бывают ситуации, когда хочется или нужно управлять DOM напрямую или особым образом.
В этой статье я расскажу о трех эффективных способах манипулирования DOM во Vue-приложениях, разберу их на примерах и поясню, когда стоит выбирать тот или иной способ, а также на что обратить внимание в плане производительности и читаемости кода.
Я сфокусируюсь на следующих методах:
- Управление отображением с помощью
v-if
иv-show
- Доступ к элементам через
$refs
- Рендеринг HTML через директиву
v-html
Давайте рассмотрим эти подходы подробнее, чтобы понять их возможности и тонкости.
Управление отображением с помощью v-if и v-show
Что такое v-if и v-show
Vue предлагает специальные директивы — v-if
и v-show
— которые позволяют контролировать присутствие элементов в DOM на основе состояния ваших данных.
v-if
полностью удаляет или добавляет элемент в DOM в зависимости от условия.v-show
всегда оставляет элемент в DOM, но управляет его видимостью через CSS-свойствоdisplay
.
Использование этих возможностей важно, когда требуется динамически показывать, скрывать или удалять элементы в зависимости от состояния приложения.
Пример использования v-if
Смотрите, вот так вы можете показать или удалить элемент из DOM в зависимости от состояния:
<template>
<div>
<button @click="visible = !visible">Показать/скрыть уведомление</button>
<div v-if="visible">
Важное уведомление!
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false // По умолчанию сообщение скрыто
}
}
}
</script>
// Когда вы кликаете по кнопке, переменная visible меняется. Если значение становится true, элемент добавляется в DOM, иначе — удаляется полностью.
Как работает v-show
А вот пример с v-show
:
<template>
<div>
<button @click="show = !show">Показать/скрыть блок</button>
<div v-show="show">
Я просто скрываю себя с помощью CSS.
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: false // По умолчанию блок скрыт
}
}
}
</script>
// Здесь элемент всегда присутствует в DOM, но видимость управляется стилем display: none. Это быстрее при частых переключениях.
Когда использовать v-if, а когда v-show
Обратите внимание, что v-if
стоит выбирать, если элемент появляется или исчезает редко и для вас важно, чтобы его не было в DOM вообще.
Для частого переключения видимости лучше подходит v-show
— элемент всегда в DOM, и переключение происходит здесь и сейчас, быстро, без пересоздания компонента.
Групповое управление с помощью
Vue разрешает использовать директивы на контейнере <template>
, если хотите показывать или скрывать сразу несколько элементов без лишней обертки:
<template>
<div>
<button @click="showDetails = !showDetails">Показать детали</button>
<template v-if="showDetails">
<p>Детальная информация A</p>
<p>Детальная информация B</p>
</template>
</div>
</template>
// Так вы не добавляете в DOM ненужные уровни вложенности.
Как еще можно усложнить пример
Вложенное использование v-if
и динамические условия:
<template>
<div>
<button @click="mode = 'a'">Режим A</button>
<button @click="mode = 'b'">Режим B</button>
<div v-if="mode === 'a'">
Сейчас выбран режим А
</div>
<div v-else-if="mode === 'b'">
Сейчас выбран режим B
</div>
<div v-else>
Ни один режим не выбран
</div>
</div>
</template>
// Можно легко делать сложные переключатели на основе состояния.
Доступ к DOM-элементам через $refs
Что такое $refs
Vue позволяет создавать специальные ссылки на элементы внутри шаблона с помощью атрибута ref
. Если вам нужно получить прямой доступ к DOM-элементу или экземпляру дочернего компонента — используйте $refs
.
Обычно такой подход нужен, когда вам нужно программно управлять фокусом, выделением текста, прокруткой, запускать анимации или интегрироваться с нелинейной сторонней библиотекой.
Как объявить ref и использовать $refs
Давайте посмотрим, как это сделать:
<template>
<div>
<input ref="myInput" type="text">
<button @click="focusInput">Фокус на поле ввода</button>
</div>
</template>
<script>
export default {
methods: {
focusInput() {
// Здесь мы обращаемся к DOM-элементу по ref и вызываем метод focus
this.$refs.myInput.focus();
}
}
}
</script>
// При клике на кнопку input получает фокус. Это очень удобно для автофокуса или кастомных взаимодействий с элементами.
// Использовать $refs желательно только когда нельзя обойтись стандартными средствами Vue и реактивных данных.
Получение DOM-элемента или компонента через $refs
Если на элементе стоит ref
, через this.$refs.refName вы получите DOM-элемент или экземпляр компонента.
- Если это обычный DOM-элемент, придет ссылка на этот элемент, как в примере выше.
- Если это дочерний компонент, получите экземпляр Vue-компонента:
<template>
<child-component ref="child"></child-component>
<button @click="callChildMethod">Запустить метод дочернего компонента</button>
</template>
<script>
export default {
methods: {
callChildMethod() {
// Вызываем публичный метод у дочернего компонента
this.$refs.child.someMethodFromChild();
}
}
}
</script>
// $refs отлично подходит для управления сложной логикой, где требуется прямой доступ к внутреннему API HTML-элементов или компонентов.
Массив $refs
Если вы используете рефы внутри v-for
, то this.$refs.<имя> будет массивом:
<template>
<ul>
<li v-for="item in items" :key="item.id" :ref="setRefs">{{ item.name }}</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [{ id: 1, name: 'One' }, { id: 2, name: 'Two' }]
}
},
methods: {
setRefs(el) {
// Vue автоматически собирает массив рефов, если один и тот же ref указывается несколько раз
}
},
mounted() {
// Теперь this.$refs.setRefs — массив элементов <li>
this.$refs.setRefs.forEach(li => {
// Можно добавить стили или обработчики каждому элементу
});
}
}
</script>
// $refs довольно гибкий, но его не стоит использовать для постоянного доступа к данным компонентов, иначе структура может стать нечитаемой.
Когда избегать $refs
$refs нужно использовать там, где действительно требуется взаимодействие с конкретными DOM-элементами. Для абстрагированного управления отображением желательно использовать реактивные данные Vue.
Рендеринг HTML с помощью v-html
Для чего нужен v-html
Если вам нужно отобразить кусок HTML, полученный динамически (например, из API или настроек), Vue предлагает директиву v-html
. Она вставляет заданную строку как HTML, а не как текст. Вот пример:
<template>
<div>
<div v-html="rawHtml"></div>
</div>
</template>
<script>
export default {
data() {
return {
rawHtml: '<b>Это выделено жирным!</b>' // HTML-код, который мы хотим отобразить
}
}
}
</script>
// В отличие от {{ rawHtml }}, где HTML выводится как текст, v-html реально создает разметку в DOM.
Особенности и предостережения
- Никогда не вставляйте в
v-html
данные, поступившие от пользователя или ненадежных источников, без санитации. Иначе ваш сайт станет уязвим для XSS-атак. - Vue не применяет дополнительные обработки — то, что вы передали в
v-html
, то попадет в DOM.
Реальный случай использования: описание из CMS
Представьте, что вы выводите описание товара из CMS, в котором могут быть ссылки или выделения:
<template>
<div>
<div v-html="productDescription"></div>
</div>
</template>
<script>
export default {
data() {
return {
productDescription: '<p>Этот <a href="#">товар</a> — отличный выбор!</p>'
}
}
}
</script>
// Контент будет "оживать" и корректно отображаться.
Можно ли динамически добавлять компоненты через v-html?
Обратите внимание, что Vue не обрабатывает шаблоны внутри v-html
как свои компоненты — внутренности не связываются с вашим реактивным состоянием. Получатся просто “сырые” DOM-узлы, которыми Vue дальше не управляет.
Заключение
В обработке DOM во Vue есть несколько проверенных подходов, которые закрывают практически все задачи по управлению разметкой и реагированию на события пользователя.
- Через
v-if
иv-show
вы удобно реагируете на изменения состояния, добавляя и убирая блоки из DOM. - С помощью
$refs
вы напрямую управляете HTML-элементами — идеально для случаев, когда стандартных реактивных средств недостаточно. - С директивой
v-html
можно быстро встраивать динамические фрагменты HTML, поступающие из внешних источников.
Важно использовать эти способы осознанно, выбирая наиболее подходящий для конкретной задачи: экономить ресурсы, поддерживать безопасность и читаемость кода.
Хорошая архитектура Vue-приложения всегда базируется на принципах реактивности — прямое взаимодействие с DOM стоит применять только там, где это действительно необходимо.
Частозадаваемые технические вопросы и ответы
Как получить доступ к родительскому компоненту из дочернего?
Используйте свойство $parent
внутри дочернего компонента. Например, this.$parent
. Однако стоит помнить, что изменение родителя таким способом увеличивает связанность компонентов и усложняет поддержку. Лучше обмениваться данными через props и события, а прямой доступ применять только при необходимости.
Как анимировать появление элементов при использовании v-if или v-show?
Добавьте обертку <transition>
, внутри которой размещайте элементы с v-if
или v-show
:
<transition name="fade">
<div v-if="visible">...</div>
</transition>
Затем определите классыAnimations CSS для fade-enter-active
, fade-leave-active
и т.д. Vue таким образом будет применять плавные переходы при появлении и исчезновении.
Можно ли управлять DOM-подсистемой сторонней библиотеки из Vue?
Да, но используйте для этого хуки жизненного цикла mounted
или updated
и $refs для доступа к DOM-узлам. Подключайте сторонние виджеты только после того, как DOM уже создан, чтобы избежать ошибок работы с еще несуществующими элементами.
Как безопасно вставлять HTML, если нужно использовать пользовательский ввод?
Перед передачей данных в v-html
обязательно очищайте их с помощью надежной библиотеки для "санитации" HTML, например, dompurify или sanitize-html. Это защитит приложение от XSS-атак.
Что делать, если $refs возвращает undefined?
Убедитесь, что компонент или элемент действительно смонтирован. Получить $refs можно только после отработки хука mounted
. Если доступ к $refs нужен сразу, дождитесь этого этапа жизненного цикла.