Кастомная функция parseQuery

Vue Router использует механизм маршрутизации на основе URL, и одним из ключевых аспектов является работа с параметрами запроса (query parameters). По умолчанию библиотека предоставляет стандартный парсер для query-параметров, однако иногда возникает необходимость создать кастомную функцию parseQuery, чтобы управлять процессом разбора строки запроса по своим правилам.

Основная концепция

Функция parseQuery отвечает за преобразование строки запроса из URL в объект JavaScript. Стандартный механизм Vue Router разбивает URL вида:

/products?category=books&page=2

в объект:

{
  category: 'books',
  page: '2'
}

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

Сигнатура функции

Кастомная parseQuery должна иметь сигнатуру:

type ParseQuery = (query: string) => Record

где query — это строка после символа ? в URL, а функция возвращает объект с ключами и значениями. Важно, чтобы функция корректно обрабатывала пустую строку и специальные символы, такие как +, %20, &, =.

Пример базовой кастомной функции

function parseQuery(queryString) {
  const query = Object.create(null);

  if (!queryString) return query;

  queryString
    .replace(/^\?/, '')
    .split('&')
    .forEach(pair => {
      const [key, value] = pair.split('=').map(decodeURIComponent);
      if (key) query[key] = value ?? '';
    });

  return query;
}

Разбор кода:

  • replace(/^\?/, '') удаляет ведущий символ ?, если он присутствует.
  • split('&') делит строку на отдельные пары ключ-значение.
  • decodeURIComponent декодирует URL-кодированные символы.
  • value ?? '' обеспечивает наличие пустой строки, если значение отсутствует.

Обработка массивов и вложенных объектов

Стандартный парсер Vue Router не поддерживает вложенные структуры по умолчанию. Кастомная функция может это исправить, используя соглашения вроде items[]=1&items[]=2 для массивов или user[name]=John&user[age]=30 для объектов.

Пример:

function parseQuery(queryString) {
  const query = {};

  if (!queryString) return query;

  queryString
    .replace(/^\?/, '')
    .split('&')
    .forEach(pair => {
      let [key, value] = pair.split('=').map(decodeURIComponent);
      if (!key) return;

      // Обработка массивов
      if (key.endsWith('[]')) {
        key = key.slice(0, -2);
        if (!query[key]) query[key] = [];
        query[key].push(value ?? '');
        return;
      }

      // Обработка вложенных объектов
      const nestedMatch = key.match(/^(\w+)\[(\w+)\]$/);
      if (nestedMatch) {
        const parent = nestedMatch[1];
        const child = nestedMatch[2];
        if (!query[parent]) query[parent] = {};
        query[parent][child] = value ?? '';
        return;
      }

      query[key] = value ?? '';
    });

  return query;
}

Эта реализация позволяет:

  • Автоматически создавать массивы, если ключ оканчивается на [].
  • Создавать вложенные объекты, если используется синтаксис ключ[подключ].
  • Сохранять привычный формат для простых ключей.

Использование кастомной функции в Vue Router

При создании экземпляра маршрутизатора можно указать собственный парсер:

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

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/products', component: Products },
    { path: '/users', component: Users }
  ],
  parseQuery(queryString) {
    return parseQuery(queryString); // наша кастомная функция
  }
});

Теперь все переходы по URL будут использовать кастомную логику разбора query-параметров.

Рекомендации по кастомизации

  1. Обработка типов данных: можно автоматически конвертировать строки 'true' и 'false' в булевы значения, числа в Number, а пустые строки в null.
  2. Безопасность: декодировать все компоненты через decodeURIComponent, чтобы предотвратить ошибки при неправильном кодировании URL.
  3. Поддержка массивов и объектов: использовать соглашения вроде [] и [ключ] для структурирования данных.
  4. Производительность: избегать сложных регулярных выражений и рекурсий для больших URL-строк, чтобы не ухудшать отклик маршрутизатора.
  5. Совместимость: тестировать работу с нативными методами браузера (URLSearchParams) и сравнивать результаты с кастомным парсером.

Пример расширенной обработки типов

function parseQuery(queryString) {
  const query = {};
  if (!queryString) return query;

  queryString
    .replace(/^\?/, '')
    .split('&')
    .forEach(pair => {
      let [key, value] = pair.split('=').map(decodeURIComponent);
      if (!key) return;

      // Конвертация типов
      if (value === 'true') value = true;
      else if (value === 'false') value = false;
      else if (!isNaN(value) && value !== '') value = Number(value);

      // Обработка массивов
      if (key.endsWith('[]')) {
        key = key.slice(0, -2);
        if (!query[key]) query[key] = [];
        query[key].push(value);
        return;
      }

      query[key] = value;
    });

  return query;
}

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