Рубрика: Javascript

  • Типизация слотов в 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 Slots передача параметров

    Slots (слоты) в Vue 3 предоставляют мощный механизм для создания гибких и переиспользуемых компонентов. В этой статье мы разберём все способы передачи параметров в слоты с примерами из реальной практики.

    Основные типы слотов в Vue 3

    • Слоты по умолчанию — базовый способ передачи контента
    • Именованные слоты — для точного позиционирования контента
    • Scoped slots — с передачей параметров из дочернего компонента

    1. Scoped Slots (Основной способ передачи параметров)

    <!-- Дочерний компонент -->
    <template>
      <div>
        <slot :item="item" :index="index"></slot>
      </div>
    </template>
    
    <!-- Родительский компонент -->
    <ChildComponent>
      <template v-slot:default="slotProps">
        {{ slotProps.item }} - {{ slotProps.index }}
      </template>
    </ChildComponent>

    2. Именованные scoped slots

    <!-- Дочерний компонент -->
    <template>
      <div>
        <slot name="header" :title="title"></slot>
        <slot name="content" :data="contentData"></slot>
      </div>
    </template>
    
    <!-- Родительский компонент -->
    <DataContainer>
      <template #header="{ title }">
        <h2>{{ title }}</h2>
      </template>
      
      <template #content="{ data }">
        <p>{{ data.description }}</p>
      </template>
    </DataContainer>

    3. Динамические параметры слотов

    <!-- Дочерний компонент -->
    <script setup>
    const slotProps = computed(() => ({
      user: currentUser.value,
      timestamp: new Date()
    }))
    </script>
    
    <template>
      <slot v-bind="slotProps"></slot>
    </template>

    4. Деструктуризация параметров

    <template #item="{ id, name }">
      <div>{{ id }}: {{ name }}</div>
    </template>

    Практические примеры

    Пример 1: Гибкий список

    <SmartList :items="users">
      <template #item="{ user }">
        <UserCard :user="user" />
      </template>
    </SmartList>

    Пример 2: Модальное окно с параметрами

    <ModalDialog>
      <template #header="{ close }">
        <button @click="close">×</button>
      </template>
      
      <template #default="{ data }">
        {{ data.message }}
      </template>
    </ModalDialog>

    Лучшие практики

    • Используйте осмысленные имена для параметров слотов
    • Для сложных компонентов документируйте структуру слотов
    • Избегайте глубокой вложенности scoped slots
    • Используйте TypeScript для типизации параметров

    Заключение

    Scoped slots в Vue 3 предоставляют мощный инструмент для создания гибких компонентов. Освоив передачу параметров в слоты, вы сможете создавать по-настоящему переиспользуемые UI-компоненты.

    Для более сложных сценариев рассмотрите использование Composition API вместе со слотами.

    Vue 3 Composition API + Slots: Сложные сценарии использования

    В сочетании с Composition API слоты Vue 3 раскрывают свою настоящую мощь. Рассмотрим продвинутые паттерны для сложных UI-компонентов.

    1. Динамические слоты с реактивными параметрами

    <!-- DynamicTable.vue -->
    <script setup>
    import { ref, computed } from 'vue'
    
    const props = defineProps(['data'])
    const sortDirection = ref('asc')
    
    const sortedData = computed(() => {
      return [...props.data].sort((a, b) => {
        return sortDirection.value === 'asc' 
          ? a.value - b.value 
          : b.value - a.value
      })
    })
    </script>
    
    <template>
      <table>
        <slot name="header" :toggleSort="() => sortDirection = sortDirection === 'asc' ? 'desc' : 'asc'"></slot>
        <tr v-for="(item, index) in sortedData" :key="index">
          <slot :item="item" :index="index"/>
        </tr>
      </table>
    </template>

    Применение: Таблицы с сортировкой, где логика инкапсулирована в компоненте, а рендеринг контролируется через слоты.

    2. Состояние модальных окон

    <!-- useModal.js -->
    import { ref } from 'vue'
    
    export function useModal() {
      const isOpen = ref(false)
      
      const open = () => isOpen.value = true
      const close = () => isOpen.value = false
      
      return { isOpen, open, close }
    }
    
    <!-- ModalComponent.vue -->
    <script setup>
    import { useModal } from './useModal'
    const modal = useModal()
    </script>
    
    <template>
      <slot :open="modal.open" :close="modal.close" :isOpen="modal.isOpen"/>
    </template>

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

    <ModalComponent v-slot="{ open, close, isOpen }">
      <button @click="open">Открыть</button>
      
      <div v-if="isOpen" class="modal">
        <slot name="content"/>
        <button @click="close">Закрыть</button>
      </div>
    </ModalComponent>

    3. Композиционные слоты для форм

    <!-- useFormField.js -->
    import { ref, computed } from 'vue'
    
    export function useFormField(initialValue) {
      const value = ref(initialValue)
      const isValid = computed(() => value.value.length > 0)
      
      return { value, isValid }
    }
    
    <!-- FormField.vue -->
    <script setup>
    import { useFormField } from './useFormField'
    
    const props = defineProps(['initialValue'])
    const field = useFormField(props.initialValue)
    </script>
    
    <template>
      <div class="form-field" :class="{ invalid: !field.isValid }">
        <slot :value="field.value" :isValid="field.isValid"/>
      </div>
    </template>

    Использование с кастомным input:

    <FormField initialValue="" v-slot="{ value, isValid }">
      <input 
        v-model="value"
        :class="{ error: !isValid }"
        placeholder="Введите текст"
      >
      <span v-if="!isValid">Поле обязательно</span>
    </FormField>

    4. Сложные компоненты данных

    <!-- DataFetcher.vue -->
    <script setup>
    import { ref, onMounted } from 'vue'
    
    const props = defineProps(['url'])
    const data = ref(null)
    const error = ref(null)
    const isLoading = ref(false)
    
    onMounted(async () => {
      isLoading.value = true
      try {
        const response = await fetch(props.url)
        data.value = await response.json()
      } catch (err) {
        error.value = err
      } finally {
        isLoading.value = false
      }
    })
    </script>
    
    <template>
      <slot 
        :data="data" 
        :error="error" 
        :isLoading="isLoading"
        :reload="onMounted"
      />
    </template>

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

    <DataFetcher url="/api/users" v-slot="{ data, isLoading }">
      <div v-if="isLoading">Загрузка...</div>
      <UserList v-else :users="data" />
    </DataFetcher>

    5. Продвинутый пример: Компонент вкладок

    <!-- TabsContainer.vue -->
    <script setup>
    import { ref } from 'vue'
    
    const activeTab = ref(0)
    const tabs = ref([])
    
    const registerTab = (title) => {
      const id = tabs.value.length
      tabs.value.push({ id, title })
      return id
    }
    
    const setActiveTab = (id) => {
      activeTab.value = id
    }
    </script>
    
    <template>
      <div class="tabs-container">
        <div class="tabs-header">
          <button 
            v-for="tab in tabs" 
            @click="setActiveTab(tab.id)"
            :class="{ active: activeTab === tab.id }"
          >
            {{ tab.title }}
          </button>
        </div>
        
        <div class="tabs-content">
          <slot :activeTab="activeTab"/>
        </div>
      </div>
    </template>

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

    <TabsContainer v-slot="{ activeTab }">
      <template #default>
        <Tab :register="registerTab" title="Профиль">
          <div v-if="activeTab === 0">...</div>
        </Tab>
        
        <Tab :register="registerTab" title="Настройки">
          <div v-if="activeTab === 1">...</div>
        </Tab>
      </template>
    </TabsContainer>

    Заключение

    Сочетание Composition API и слотов позволяет создавать:

    • Полностью инкапсулированную логику компонентов
    • Максимально гибкие API для переиспользуемых компонентов
    • Сложные stateful-компоненты с простым интерфейсом
    • Легко тестируемые решения (бизнес-логика отделена от рендеринга)

  • Быстрая сортировка (Quick Sort) на PHP и JavaScript

    Быстрая сортировка — один из самых эффективных алгоритмов сортировки с средней сложностью O(n log n). В этой статье разберём принцип работы и реализации на PHP и JavaScript.

    📌 Принцип работы Quick Sort

    • Выбор опорного элемента (pivot)
    • Разделение массива на две части: элементы меньше pivot и больше pivot
    • Рекурсивная сортировка обеих частей

    💻 Реализация Quick Sort на PHP

    <?php
    function quickSort(array $arr): array {
        if (count($arr) <= 1) {
            return $arr;
        }
        
        $pivot = $arr[0];
        $left = $right = [];
        
        for ($i = 1; $i < count($arr); $i++) {
            if ($arr[$i] < $pivot) {
                $left[] = $arr[$i];
            } else {
                $right[] = $arr[$i];
            }
        }
        
        return array_merge(quickSort($left), [$pivot], quickSort($right));
    }
    
    // Пример использования
    $array = [10, 80, 30, 90, 40, 50, 70];
    $sorted = quickSort($array);
    print_r($sorted);
    ?>

    🌐 Реализация Quick Sort на JavaScript

    1. Классическая реализация

    function quickSort(arr) {
        if (arr.length <= 1) return arr;
        
        const pivot = arr[0];
        const left = [];
        const right = [];
        
        for (let i = 1; i < arr.length; i++) {
            if (arr[i] < pivot) {
                left.push(arr[i]);
            } else {
                right.push(arr[i]);
            }
        }
        
        return [...quickSort(left), pivot, ...quickSort(right)];
    }
    
    // Пример использования
    const array = [10, 80, 30, 90, 40, 50, 70];
    console.log(quickSort(array));

    2. Оптимизированная версия (in-place)

    function quickSortInPlace(arr, left = 0, right = arr.length - 1) {
        if (left >= right) return;
        
        const pivot = partition(arr, left, right);
        quickSortInPlace(arr, left, pivot - 1);
        quickSortInPlace(arr, pivot + 1, right);
        
        return arr;
    }
    
    function partition(arr, left, right) {
        const pivot = arr[right];
        let i = left;
        
        for (let j = left; j < right; j++) {
            if (arr[j] < pivot) {
                [arr[i], arr[j]] = [arr[j], arr[i]];
                i++;
            }
        }
        
        [arr[i], arr[right]] = [arr[right], arr[i]];
        return i;
    }
    
    // Пример использования
    const nums = [10, 80, 30, 90, 40, 50, 70];
    console.log(quickSortInPlace([...nums]));

    ⚡ Сравнение Quick Sort с другими алгоритмами

    ХарактеристикаQuick SortMerge SortBubble Sort
    Средняя сложностьO(n log n)O(n log n)O(n²)
    Худший случайO(n²)O(n log n)O(n²)
    ПамятьO(log n)O(n)O(1)
    СтабильностьНетДаДа

    📌 Когда использовать Quick Sort?

    • Преимущества:
      • Обычно быстрее других алгоритмов на практике
      • Требует мало дополнительной памяти
      • Хорошо работает с кэшем процессора
    • Недостатки:
      • Худший случай O(n²) (редко при правильном выборе pivot)
      • Нестабильный

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

    🔍 Дополнение: Нестабильность Quick Sort на практике

    Критически важное отличие Quick Sort от стабильных алгоритмов вроде Merge Sort — его нестабильность. Это означает, что при наличии одинаковых элементов их относительный порядок после сортировки может измениться.

    Наглядный пример

    // Данные: пользователи с одинаковым возрастом
    const users = [
      { name: 'Анна', age: 25 },
      { name: 'Иван', age: 30 },
      { name: 'Мария', age: 25 } // Такое же значение age, как у Анны
    ];
    
    // После Quick Sort возможен вариант:
    [
      { name: 'Мария', age: 25 }, // Мария теперь перед Анной!
      { name: 'Анна', age: 25 },
      { name: 'Иван', age: 30 }
    ]

    Когда это критично?

    • При сортировке таблиц по нескольким столбцам (например: сначала по дате, потом по имени)
    • В финансовых системах, где важен порядок операций с одинаковой суммой
    • При работе с хронологическими данными (новости, события)

    Как решить проблему?

    1. Добавить индекс сравнения:
      arr.map((item, index) => ({ ...item, _index: index }))
      // Сортировка с учётом _index при равенстве
    2. Использовать стабильные аналоги:
      • Merge Sort
      • Встроенный Array.prototype.sort() (стабилен в современных браузерах)
      • Timsort (Python, Java)
    СитуацияРекомендация
    Важна скорость + нет одинаковых ключейQuick Sort (оптимальный выбор)
    Работа с объектами/дублямиMerge Sort или встроенная сортировка
    Ограниченная памятьIn-place Quick Sort (но без стабильности)

    Это дополнение помогает понять, почему в некоторых случаях разработчики предпочитают Merge Sort, несмотря на его повышенные требования к памяти.

  • Сортировка слиянием (Merge Sort) на PHP и JavaScript

    Сортировка слиянием — это эффективный алгоритм сортировки, работающий по принципу «разделяй и властвуй». Он обладает стабильностью и гарантированной сложностью O(n log n).

    📌 Алгоритм сортировки слиянием

    • Разделение массива на две половины
    • Рекурсивная сортировка каждой половины
    • Слияние отсортированных половин

    💻 Реализация на PHP

    <?php
    function mergeSort(array $arr): array {
        if (count($arr) <= 1) {
            return $arr;
        }
        
        $middle = (int)(count($arr) / 2);
        $left = mergeSort(array_slice($arr, 0, $middle));
        $right = mergeSort(array_slice($arr, $middle));
        
        return merge($left, $right);
    }
    
    function merge(array $left, array $right): array {
        $result = [];
        $i = $j = 0;
        
        while ($i < count($left) && $j < count($right)) {
            if ($left[$i] < $right[$j]) {
                $result[] = $left[$i++];
            } else {
                $result[] = $right[$j++];
            }
        }
        
        return array_merge($result, array_slice($left, $i), array_slice($right, $j));
    }
    
    // Пример использования
    $array = [38, 27, 43, 3, 9, 82, 10];
    $sorted = mergeSort($array);
    print_r($sorted);
    ?>

    🌐 Реализация на JavaScript

    1. Классическая версия

    function mergeSort(arr) {
        if (arr.length <= 1) return arr;
        
        const middle = Math.floor(arr.length / 2);
        const left = mergeSort(arr.slice(0, middle));
        const right = mergeSort(arr.slice(middle));
        
        return merge(left, right);
    }
    
    function merge(left, right) {
        let result = [];
        let i = 0, j = 0;
        
        while (i < left.length && j < right.length) {
            if (left[i] < right[j]) {
                result.push(left[i++]);
            } else {
                result.push(right[j++]);
            }
        }
        
        return result.concat(left.slice(i)).concat(right.slice(j));
    }
    
    // Пример использования
    const array = [38, 27, 43, 3, 9, 82, 10];
    console.log(mergeSort(array));

    2. Версия с визуализацией шагов

    function mergeSortWithSteps(arr, level = 0) {
        console.log(`${'  '.repeat(level)}Сортируем:`, arr);
        
        if (arr.length <= 1) {
            console.log(`${'  '.repeat(level)}Базовый случай:`, arr);
            return arr;
        }
        
        const middle = Math.floor(arr.length / 2);
        const left = mergeSortWithSteps(arr.slice(0, middle), level + 1);
        const right = mergeSortWithSteps(arr.slice(middle), level + 1);
        
        const merged = merge(left, right);
        console.log(`${'  '.repeat(level)}Сливаем ${left} и ${right} →`, merged);
        
        return merged;
    }
    
    const nums = [5, 2, 4, 7, 1];
    console.log("Исходный массив:", nums);
    console.log("Результат:", mergeSortWithSteps(nums));

    ⚡ Сравнение с другими алгоритмами

    ХарактеристикаMerge SortQuick SortBubble Sort
    Сложность (средняя)O(n log n)O(n log n)O(n²)
    Сложность (худшая)O(n log n)O(n²)O(n²)
    ПамятьO(n)O(log n)O(1)
    СтабильностьДаНетДа

    📌 Когда использовать?

    • Плюсы Merge Sort:
      • Гарантированная сложность O(n log n)
      • Стабильность (сохраняет порядок равных элементов)
      • Хорош для связных списков и внешней сортировки
    • Минусы:
      • Требует дополнительной памяти O(n)
      • Медленнее Quick Sort на небольших массивах

    Оптимальное применение: большие массивы (от 10k элементов), где важна стабильность.

    Другие виды сортировок

    • Quick Sort — быстрая сортировка
    • Bubble Sort — сортировка пузырьком
  • Сортировка пузырьком в PHP и JavaScript: примеры и объяснение

    Сортировка пузырьком (Bubble Sort) — один из самых простых алгоритмов сортировки, который отлично подходит для обучения основам алгоритмов. В этой статье мы разберём:

    • Как работает сортировка пузырьком
    • Реализацию на PHP 8+ и JavaScript (ES6+)
    • Оптимизацию алгоритма
    • Примеры с пошаговым выводом

    📌 Как работает сортировка пузырьком?

    Алгоритм последовательно сравнивает соседние элементы массива и меняет их местами, если они расположены в неправильном порядке. Этот процесс повторяется до тех пор, пока массив не будет полностью отсортирован.

    Основные шаги:

    • Проход по массиву от начала до конца.
    • Сравнение текущего элемента (arr[j]) со следующим (arr[j+1]).
    • Если arr[j] > arr[j+1], меняем их местами.
    • Повторяем, пока весь массив не станет упорядоченным.

    🔹 Временная сложность:

    • Худший случай: O(n²) (если массив отсортирован в обратном порядке).
    • Лучший случай: O(n) (если массив уже отсортирован и используется оптимизация).

    💻 Реализация на PHP (версия 8.0+)

    <?php
    function bubbleSort(array $arr): array {
        $n = count($arr);
        for ($i = 0; $i < $n; $i++) {
            $swapped = false; // Флаг для оптимизации
            for ($j = 0; $j < $n - $i - 1; $j++) {
                if ($arr[$j] > $arr[$j + 1]) {
                    // Обмен значений (деструктуризация в PHP 7.1+)
                    [$arr[$j], $arr[$j + 1]] = [$arr[$j + 1], $arr[$j]];
                    $swapped = true;
                }
            }
            // Если обменов не было, массив уже отсортирован
            if (!$swapped) break;
        }
        return $arr;
    }
    
    // Пример использования
    $array = [64, 34, 25, 12, 22, 11, 90];
    $sortedArray = bubbleSort($array);
    echo "Отсортированный массив: " . implode(", ", $sortedArray);
    ?>

    🔸 Пояснение:

    • Используется деструктуризация ([$a, $b] = [$b, $a]), доступная с PHP 7.1+.
    • Флаг $swapped позволяет досрочно завершить сортировку, если массив уже упорядочен.

    Вывод:

    Отсортированный массив: 11, 12, 22, 25, 34, 64, 90

    🌐 Реализация на JavaScript (ES6+)

    1. Базовая версия

    function bubbleSort(arr) {
        const n = arr.length;
        for (let i = 0; i < n; i++) {
            let swapped = false;
            for (let j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // Обмен значений (деструктуризация в ES6)
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                    swapped = true;
                }
            }
            if (!swapped) break;
        }
        return arr;
    }
    
    const array = [64, 34, 25, 12, 22, 11, 90];
    console.log("Отсортированный массив:", bubbleSort(array));

    Вывод:

    [11, 12, 22, 25, 34, 64, 90]

    2. Версия с пошаговым выводом (для обучения)

    function bubbleSortWithSteps(arr) {
        const n = arr.length;
        for (let i = 0; i < n; i++) {
            let swapped = false;
            console.log(`🔹 Проход ${i + 1}:`);
            for (let j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                    swapped = true;
                    console.log(`   Поменяли ${arr[j]} ↔ ${arr[j + 1]}:`, [...arr]);
                }
            }
            if (!swapped) break;
        }
        return arr;
    }
    
    const nums = [5, 3, 8, 4, 2];
    console.log("Исходный массив:", nums);
    bubbleSortWithSteps(nums);
    console.log("Результат:", nums);

    Вывод:

    Исходный массив: [5, 3, 8, 4, 2]
    🔹 Проход 1:
       Поменяли 3 ↔ 5: [3, 5, 8, 4, 2]
       Поменяли 4 ↔ 8: [3, 5, 4, 8, 2]
       Поменяли 2 ↔ 8: [3, 5, 4, 2, 8]
    🔹 Проход 2:
       Поменяли 4 ↔ 5: [3, 4, 5, 2, 8]
       Поменяли 2 ↔ 5: [3, 4, 2, 5, 8]
    🔹 Проход 3:
       Поменяли 2 ↔ 4: [3, 2, 4, 5, 8]
    🔹 Проход 4:
       Поменяли 2 ↔ 3: [2, 3, 4, 5, 8]
    Результат: [2, 3, 4, 5, 8]

    📌 Итог

    • PHP 8+ и JavaScript (ES6+) поддерживают современный синтаксис (деструктуризацию).
    • Оптимизация с флагом swapped ускоряет сортировку в лучшем случае до O(n).
    • Сложность в худшем случае — O(n²), поэтому для больших массивов лучше использовать быструю сортировку QuickSort, сортировку слиянием MergeSort или встроенные методы (sort()).

    🚀 Где применять?

    • Для обучения алгоритмам.
    • Для небольших массивов (до 1000 элементов).

    📢 Ваше мнение:

    Пользуетесь ли вы сортировкой пузырьком в реальных проектах? Делитесь в комментариях!

  • Реактивность в 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
    • Мониторьте производительность сложных эффектов

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

  • Методы массивов в JavaScript для поиска и проверки элементов в массиве: some, every, includes, find, findIndex.

    В JavaScript у массивов есть несколько методов для проверки элементов. В этой статье разберём:

    • some() — проверяет, удовлетворяет ли хотя бы один элемент условию.
    • Похожие методы: every(), includes(), find(), findIndex().
    • Примеры и сравнение с аналогами.

    1. Array.some(): Хотя бы один элемент проходит проверку

    Синтаксис

    arr.some(callback(element, index, array));

    — Возвращает true, если хотя бы один элемент соответствует условию.
    — Иначе — false.

    Пример

    const numbers = [1, 2, 3, 4, 5];
    
    // Есть ли хотя бы одно чётное число?
    const hasEven = numbers.some(num => num % 2 === 0);
    console.log(hasEven); // true (2 и 4 подходят)

    Когда использовать?

    Проверка, что в массиве есть хотя бы один подходящий элемент.


    2. Array.every(): Все элементы проходят проверку

    Синтаксис

    arr.every(callback(element, index, array));

    — Возвращает true, если все элементы удовлетворяют условию.

    Пример

    const ages = [18, 22, 25, 30];
    
    // Все ли совершеннолетние?
    const allAdults = ages.every(age => age >= 18);
    console.log(allAdults); // true

    Сравнение some() и every()

    МетодВозвращает true, если…Аналог в логике
    some()Хотя бы один элемент подходит|| (ИЛИ)
    every()Все элементы подходят&& (И)

    3. Array.includes(): Проверка наличия конкретного значения

    Синтаксис

    arr.includes(value, fromIndex);

    — Проверяет, есть ли конкретное значение в массиве.

    Пример

    const fruits = ['apple', 'banana', 'orange'];
    
    console.log(fruits.includes('banana')); // true
    console.log(fruits.includes('grape'));  // false

    includes() vs some()

    includes() — ищет конкретное значение.
    some() — проверяет условие (например, через функцию).

    // Эквивалентные проверки:
    const numbers = [1, 2, 3];
    
    // Через includes()
    numbers.includes(2); // true
    
    // Через some()
    numbers.some(num => num === 2); // true

    4. Array.find() и findIndex(): Поиск первого подходящего элемента

    find()

    Возвращает первый элемент, удовлетворяющий условию:

    const users = [
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' },
    ];
    
    const bob = users.find(user => user.name === 'Bob');
    console.log(bob); // { id: 2, name: 'Bob' }

    findIndex()

    Возвращает индекс первого подходящего элемента (или -1):

    const index = users.findIndex(user => user.name === 'Bob');
    console.log(index); // 1

    Сравнение с some()

    МетодВозвращаетПодходит для…
    some()true/falseПроверка наличия
    find()Элемент или undefinedПолучение объекта
    findIndex()Индекс или -1Удаление/замена элемента

    5. Итог: Какой метод выбрать?

    ЗадачаМетод
    Есть ли хотя бы один подходящий?some()
    Все элементы подходят?every()
    Есть ли конкретное значение?includes()
    Найти первый подходящий элементfind()
    Найти индекс элементаfindIndex()

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

    ❶ Проверка прав доступа

    const permissions = ['read', 'write', 'delete'];
    
    // Есть ли право на запись?
    const canWrite = permissions.some(perm => perm === 'write');

    ❷ Валидация формы

    const inputs = ['', 'test@example.com', '123'];
    
    // Все ли поля заполнены?
    const isValid = inputs.every(input => input.trim() !== '');

    ❸ Поиск в массиве объектов

    const products = [
      { id: 1, name: 'Laptop', inStock: true },
      { id: 2, name: 'Phone', inStock: false },
    ];
    
    // Есть ли хотя бы один товар в наличии?
    const hasStock = products.some(product => product.inStock);

    Вывод

    some() — лучший выбор для проверки хотя бы одного элемента.
    every() — если нужно убедиться, что все элементы подходят.
    includes() — для простой проверки значений.
    find()/findIndex() — если нужен сам элемент или его индекс.

    Используйте эти методы, чтобы писать чистый и эффективный код! 🚀

  • Интеграция i18n с Pinia во Vue3: полное руководство

    В этой статье мы разберем, как эффективно использовать систему интернационализации (i18n) вместе с хранилищами Pinia во Vue.js приложениях.

    1. Установка и базовая настройка

    npm install pinia vue-i18n
    # или
    yarn add pinia vue-i18n

    2. Создание локалей

    Создайте JSON-файлы с переводами:

    // locales/ru.json
    {
      "cart": {
        "title": "Корзина",
        "empty": "Ваша корзина пуста"
      }
    }

    3. Инициализация i18n

    // i18n.js
    import { createI18n } from 'vue-i18n';
    import ru from './locales/ru.json';
    
    export default createI18n({
      locale: 'ru',
      fallbackLocale: 'en',
      messages: { ru }
    });

    4. Создание хранилища с i18n

    // stores/cartStore.js
    import { defineStore } from 'pinia';
    import { useI18n } from 'vue-i18n';
    
    export const useCartStore = defineStore('cart', {
      state: () => ({ items: [] }),
      getters: {
        cartTitle: () => {
          const { t } = useI18n();
          return t('cart.title');
        }
      },
      actions: {
        showEmptyMessage() {
          const { t } = useI18n();
          alert(t('cart.empty'));
        }
      }
    });

    5. Использование в компонентах

    <script setup>
    import { useCartStore } from '@/stores/cartStore';
    const cartStore = useCartStore();
    </script>
    
    <template>
      <h1>{{ cartStore.cartTitle }}</h1>
      <button @click="cartStore.showEmptyMessage">
        Проверить корзину
      </button>
    </template>

    6. Переключение языков

    // stores/localeStore.js
    export const useLocaleStore = defineStore('locale', {
      actions: {
        setLocale(lang) {
          const { locale } = useI18n();
          locale.value = lang;
        }
      }
    });

    7. Частые проблемы и решения

    • Ошибка «useI18n called outside setup()» — убедитесь, что используете хук только в setup()
    • Нет реактивности — оберните переводы в computed()

    8. Альтернативные подходы

    • Использование provide/inject для i18n
    • Создание отдельного хранилища для локализации
    • Использование composable-функций