Что, если бы ваш клиентский JavaScript мог выполнять маршрутизацию на основе регулярного выражения или функционального сопоставления запрошенных URL-адресов? Что, если бы он мог водопад, как Экспресс на сервере? В этой статье рассматриваются методы, используемые для создания такого маршрутизатора, если вы хотите его для React или Vue.

Во-первых, вот пример расширенной конфигурации маршрута. Очевидно, вам придется настроить его, если вы хотите, чтобы он был совместим по форме с существующими маршрутами React или Vue.

{
  [url => url.username && url.password ? url : undefined]: 
    function(url,done) {
      login(url.username,url.password);
    }; // route waterfalls because done not called
  },
  "/home": function(_,done)
  {  
      done(true); // this is the final route
      ... return/render home view or template ...
  },
  "/profile/:userid": function({userid},done)
  { 
      done(true); // this is the final route
      ... return/render view or template ... 
  },
  [/.*\/silent\/.*]: function(url,done) {
  {
    ... do something with silent url ...
    done(); // don't push a new history state
    // don't change view
  }
}

Для многих читателей использование квадратных скобок для инициализации свойств объекта может быть новым. Именно эта возможность упрощает определение расширенных маршрутов. Парсер JavaScript делает всю тяжелую работу! Просто укажите допустимую функцию или регулярное выражение, и JavaScript автоматически преобразует ее в строку и будет использовать ее в качестве имени свойства.

Для функций, используемых для сопоставления маршрутов, передается маршрутизируемый URL. Для регулярных выражений передается URL href.

Маршрутизированные функции принимают два аргумента.

Первый аргумент - это объект со свойствами, названными так же, как параметры маршрута, если они существуют. Или, в случае функционального маршрута, возвращаемое значение функции маршрутизации. И, в случае маршрута регулярного выражения, маршрутизируемый объект URL.

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

Теперь фактически начинается определение маршрутизации. Для простоты приведенный ниже JavaScript не включает обработку ошибок, предоставляется в монолитной форме и не предназначен специально для совместимости с Vue или React:

function route(event,routes) {
   let routeStopped;
   const url = event.target.href,
     pathname = url.pathname,
     done = (pushHistory) => {
       routeStopped = true;
       if(pushHistory) {
          history.pushState({},"",document.location.href);
       }
     };
   // check each route and see if the target path matches
   // use a conventional for loop so we can "break" or "continue"
   for(let routePath in routes) {
     let match = routePath;
     try {
       // try to convert key back to a function or RegEexp
       // safe because we define the router
       match = Function("return " + routePath)();
     } catch(e) {
       ; // ignore
     }
     const type = typeof(match);
     let args = {};
     if(type==="function" && (args = match(url)) {
     // property was a function and returns
     // a truthy value to be used as args to routed function
       routes[routePath].call(event,args,done);
       if(routeStopped) break;
       else continue; // waterfall to next route
     }
     if(type=="object" && match instanceof RegExp 
        && match.test(pathname)) {
     // property was a RegExp that matched
     // and target URL is used as routed function arg
       routes[routePath].call(event,url,done);
       if(routeStopped) break;
       else continue; // waterfall to next route
     }
     // else match is a string
     const matchParts = routePath.split("/"),
       pathParts = pathname.split("/");
     // if paths not same length, try next route
     if(pathParts.length!==matchParts.length) continue;
     // if every part of the paths generates a match or param
     // then call routed function with extracted params
     if(matchParts.every((match,i) => {
       if(match[0]===":") { // extract param
         args[match.substring(1)] = pathParts[i];
         return true;
       }
       return match===pathParts[i]; // path parts must match
       })) {
     routes[routePath].call(event,args,done);
     if(routeStopped) break;
     //else waterfall to next route
   }
}

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

Хлопайте по этой статье, если вы нашли вышеупомянутое полезным.

Если вы хотите поэкспериментировать с интерфейсной библиотекой, которая уже поддерживает расширенную маршрутизацию, попробуйте TLX, который в сжатом и сжатом формате до 5K также поддерживает:

  1. преобразование типов и анализ объектов для маршрутов и параметров строки запроса
  2. шаблонные литералы вместо JSX;
  3. многокорневые шаблоны с использованием HTML, JavaScript или удаленных URL-адресов;
  4. t-for, t-foreach, t-forvalues ​​с поддержкой повторяющегося протокола;
  5. директива атрибута t-if;
  6. директивы настраиваемых атрибутов всего в одной строке кода,
  7. необязательная двусторонняя привязка данных;
  8. автоматическое или ручное создание пользовательских элементов и компонентов, соответствующих стандартам;
  9. обработчики событий, соответствующие стандартам;
  10. обратные вызовы расширенного жизненного цикла;
  11. автоматическая защита от HTML-инъекций.