Метка: TypeScript

  • Типизация слотов в Vue 3: Как исправить ошибку TS7006 в TypeScript

    При работе с Vue 3 и TypeScript разработчики часто сталкиваются с ошибкой TS7006: Parameter implicitly has an 'any' type при использовании scoped-слотов. В этой статье разберём, как правильно типизировать слоты и избежать этой проблемы.

    Почему возникает ошибка TS7006?

    Ошибка появляется, когда TypeScript не может определить тип параметров слота:

    <template #export="slotProps">
      <ExportButton v-bind="slotProps" />
    </template>

    TypeScript выдаёт: TS7006: Parameter 'slotProps' implicitly has an 'any' type

    Решение: Явная типизация слотов

    Способ 1: Указание типа для объекта

    <template #export="props: ExportSlotProps">
      <ExportButton v-bind="props" />
    </template>

    Способ 2: Деструктуризация с типами (рекомендуется)

    <template #export="{
      getVisibleColumns,
      prepareFilterParams,
      page,
      totalItems
    }: ExportSlotProps">
      <ExportButton
        :getVisibleColumns="getVisibleColumns"
        :prepareFilterParams="prepareFilterParams"
        :page="page"
        :total-items="totalItems"
      />
    </template>

    Настройка типов в дочернем компоненте

    Для автоматического определения типов добавьте в компонент:

    <script setup lang="ts">
    import type { VNode } from 'vue'
    
    interface ExportSlotProps {
      getVisibleColumns: () => string[]
      prepareFilterParams: () => Record<string, unknown>
      page: string
      totalItems: number
    }
    
    defineSlots<{
      export?: (props: ExportSlotProps) => VNode[]
    }>()
    </script>

    Заключение

    Для устранения TS7006:

    • Используйте явную типизацию в шаблоне
    • Применяйте defineSlots() в дочерних компонентах
    • Выносите общие типы в отдельные файлы
  • Реактивность в Vue 3: watch, watchEffect и продвинутые техники

    Vue 3 представляет мощную систему реактивности, которая является фундаментом для создания современных веб-приложений. В этом руководстве мы глубоко погрузимся в механизмы отслеживания изменений, рассмотрим все аспекты работы watch и watchEffect, а также изучим продвинутые паттерны работы с реактивностью.

    Основы реактивности в Vue 3

    Vue 3 полностью переработал систему реактивности, используя JavaScript Proxy вместо Object.defineProperty. Это обеспечивает:

    • Поддержку работы с массивами и коллекциями
    • Более эффективное отслеживание изменений
    • Возможность создания «сырых» (raw) объектов без реактивности
    • Лучшую интеграцию с TypeScript

    Как работает реактивность

    import { reactive, effect } from 'vue'
    
    const state = reactive({
      count: 0
    })
    
    // Аналог watchEffect в системе реактивности
    effect(() => {
      console.log('Count:', state.count)
    })

    watch vs watchEffect: полное сравнение

    watchEffect

    • Автоматическое отслеживание зависимостей
    • Немедленный запуск при создании
    • Не предоставляет старые значения
    • Идеален для побочных эффектов
    const count = ref(0)
    
    watchEffect(() => {
      console.log(`Count changed: ${count.value}`)
    })

    watch

    • Явное указание источников
    • Ленивое выполнение (по умолчанию)
    • Предоставляет старые и новые значения
    • Подходит для сравнения состояний
    watch(count, (newVal, oldVal) => {
      console.log(`Count changed from ${oldVal} to ${newVal}`)
    })

    Продвинутые техники работы с реактивностью

    1. Контроль зависимостей

    const condition = ref(false)
    const a = ref(1)
    const b = ref(2)
    
    watchEffect(() => {
      // Только condition будет зависимостью
      if (condition.value) {
        console.log(a.value + b.value) // a и b не станут зависимостями
      }
    })

    2. Глубокое наблюдение с кастомным сравнением

    watch(
      () => ({ ...complexObject }),
      (newVal, oldVal) => {
        // Логика сравнения
      },
      {
        deep: true,
        equals: (a, b) => 
          a.id === b.id && a.items.length === b.items.length
      }
    )

    3. Реактивные цепочки и оптимизация

    const searchQuery = ref('')
    const filters = reactive({ 
      category: '', 
      sort: 'asc',
      // 10K элементов
      items: hugeArray 
    })
    
    // Оптимизированный watch
    watch(
      () => ({
        query: searchQuery.value,
        category: filters.category,
        sort: filters.sort
      }),
      ({ query, category, sort }) => {
        // Фильтрация без отслеживания hugeArray
        filterItems(query, category, sort)
      }
    )

    Практические примеры из реальных проектов

    1. Интеграция с API

    const pagination = reactive({
      page: 1,
      size: 20,
      total: 0
    })
    
    const fetchData = async () => {
      const res = await api.get('/items', {
        params: {
          page: pagination.page,
          size: pagination.size
        }
      })
      pagination.total = res.total
    }
    
    // Автоматический запрос при изменении пагинации
    watch([() => pagination.page, () => pagination.size], fetchData, {
      immediate: true
    })

    2. Управление состоянием формы

    const form = reactive({
      email: '',
      password: '',
      errors: {}
    })
    
    watch(
      () => form.email,
      (email) => {
        form.errors.email = validateEmail(email) 
          ? null 
          : 'Invalid email format'
      },
      { debounce: 300 }
    )

    Производительность и отладка

    1. Измерение времени выполнения

    watchEffect((onCleanup) => {
      const start = performance.now();
      
      // Тяжелая операция
      processLargeData();
      
      const duration = performance.now() - start;
      if (duration > 50) {
        console.warn(`Slow effect: ${duration.toFixed(2)}ms`);
      }
      
      onCleanup(() => {
        // Очистка ресурсов
      })
    })

    2. Визуализация зависимостей

    function trackDependencies(effect) {
      const deps = new Set()
      
      const reactiveEffect = watchEffect(() => {
        effect()
        console.log('Dependencies:', [...deps])
        deps.clear()
      }, {
        onTrack(e) {
          deps.add(e.target[e.key])
        }
      })
      
      return reactiveEffect
    }

    Заключение и лучшие практики

    • Используйте watch когда нужны старые значения или точный контроль
    • Выбирайте watchEffect для автоматического отслеживания зависимостей
    • Оптимизируйте глубокое наблюдение с помощью кастомных функций сравнения
    • Разделяйте сложные эффекты на несколько простых watchers
    • Всегда очищайте ресурсы в onCleanup
    • Мониторьте производительность сложных эффектов

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