Vue.js

Vue.js строится вокруг системы реактивности, в которой состояние приложения автоматически связывается с DOM. Изменение данных приводит к автоматическому обновлению представления без ручного вмешательства в DOM-операции.

Реактивность в Vue основана на проксировании объектов состояния. В современных версиях (Vue 3) используется Proxy, позволяющий отслеживать чтение и запись свойств объекта:

import { reactive } from 'vue'

const state = reactive({
  count: 0
})

state.count++

Каждое изменение state.count фиксируется системой и триггерит обновление всех зависимостей, которые используют это значение.

Ключевое свойство реактивной модели — автоматическое отслеживание зависимостей. Компонент не подписывается вручную на изменения; Vue определяет зависимости во время выполнения функции рендера.


Компонентная архитектура и структура приложения

Vue.js организует приложение как дерево компонентов. Каждый компонент инкапсулирует логику, шаблон и стили.

Базовая структура компонента:

export default {
  name: 'CounterComponent',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}

Компонент разделяет три ключевых слоя:

  • data — состояние
  • methods — поведенческая логика
  • template — декларативное описание UI

В современном Vue предпочтение отдаётся Composition API, где логика группируется по функциональному признаку, а не по опциям объекта.


Composition API и логическая декомпозиция

Composition API вводит функцию setup, в которой формируется реактивное состояние и поведение компонента.

import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    const increment = () => {
      count.value++
    }

    return {
      count,
      increment
    }
  }
}

Использование ref и reactive позволяет строить гибкие структуры состояния.

Основные преимущества подхода:

  • группировка логики по функциональности
  • повторное использование через composables
  • упрощение масштабирования сложных компонентов

Директивы и управление DOM

Vue предоставляет набор встроенных директив для декларативного управления DOM.

v-bind и динамические атрибуты

<img v-bind:src="imageUrl" />

Сокращённая форма:

<img :src="imageUrl" />

v-model и двусторонняя привязка

<input v-model="text" />

v-model связывает значение input с реактивным состоянием.

v-if и v-show

<div v-if="isVisible">Контент</div>
<div v-show="isVisible">Контент</div>

Разница:

  • v-if добавляет/удаляет элементы из DOM
  • v-show управляет CSS display

v-for и списки

<li v-for="item in items" :key="item.id">
  {{ item.name }}
</li>

Ключ key обеспечивает стабильную идентификацию элементов при обновлении списка.


Жизненный цикл компонентов

Компоненты Vue проходят ряд фаз от создания до удаления. Эти фазы позволяют подключать побочные эффекты и управлять ресурсами.

Основные хуки жизненного цикла:

  • создание состояния
  • монтирование в DOM
  • обновление при изменении данных
  • уничтожение компонента

Пример использования:

import { onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('Компонент смонтирован')
    })

    onUnmounted(() => {
      console.log('Компонент уничтожен')
    })
  }
}

Жизненный цикл критичен при работе с:

  • таймерами
  • подписками на события
  • сетевыми запросами

Вычисляемые свойства и кэширование

Computed properties позволяют создавать производные значения на основе реактивного состояния с автоматическим кэшированием.

import { ref, computed } from 'vue'

const price = ref(100)
const quantity = ref(2)

const total = computed(() => {
  return price.value * quantity.value
})

Особенности:

  • значение пересчитывается только при изменении зависимостей
  • результат кэшируется
  • оптимизирует производительность по сравнению с методами

Watchers и реакция на изменения

Watchers используются для отслеживания изменений конкретных реактивных значений.

import { ref, watch } from 'vue'

const search = ref('')

watch(search, (newValue, oldValue) => {
  console.log('Изменение:', oldValue, '→', newValue)
})

Watch применяется при необходимости:

  • выполнения асинхронных запросов
  • сложной логики реакции
  • интеграции с внешними API

Управление состоянием приложения

При росте приложения локального состояния компонентов становится недостаточно. Используются централизованные хранилища, наиболее распространённое — Pinia.

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++
    }
  }
})

Хранилище обеспечивает:

  • единый источник состояния
  • предсказуемость изменений
  • удобство масштабирования

Маршрутизация и работа с Vue Router

Vue Router отвечает за навигацию между страницами без перезагрузки.

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

Навигация реализуется через:

<router-link to="/about">О странице</router-link>
<router-view />

Router позволяет строить SPA-архитектуру с клиентским управлением маршрутами.


Обработка событий и взаимодействие компонентов

События в Vue обрабатываются через директиву v-on:

<button @click="handleClick">Нажать</button>

Передача данных между компонентами осуществляется через:

  • props (вниз по дереву)
  • emit (вверх по дереву)
export default {
  props: ['title'],
  emits: ['update']
}

Такой подход обеспечивает однонаправленный поток данных.


Слоты и композиция интерфейсов

Слоты позволяют передавать разметку в компонент извне.

<MyCard>
  <h1>Заголовок</h1>
</MyCard>

Внутри компонента:

<template>
  <div class="card">
    <slot />
  </div>
</template>

Слоты поддерживают:

  • именованные области
  • scoped slots с доступом к данным компонента

Асинхронные операции и работа с API

Vue не ограничивает способ взаимодействия с сервером. Обычно используется fetch или axios.

import { ref, onMounted } from 'vue'

const data = ref(null)

onMounted(async () => {
  const response = await fetch('/api/items')
  data.value = await response.json()
})

Асинхронные операции часто комбинируются с состояниями загрузки и ошибок:

const loading = ref(false)
const error = ref(null)

Масштабирование архитектуры приложения

При увеличении сложности проекта структура начинает включать:

  • отдельные слои сервисов
  • composables для переиспользуемой логики
  • модули состояния
  • маршрутизированные зоны приложения

Composable-функции:

export function useCounter() {
  const count = ref(0)
  const increment = () => count.value++

  return { count, increment }
}

Такой подход снижает связанность компонентов и повышает повторное использование логики.