Серия тем JS — Маршрутизация внешнего интерфейса

внешний интерфейс
Серия тем JS — Маршрутизация внешнего интерфейса

1. Что такое маршрутизация

Маршрутизация предназначена для отображения различного контента или страниц в соответствии с разными URL-адресами.

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

Позже страница становилась все более сложной, а нагрузка на сервер увеличивалась.С появлением ajax (технология асинхронного обновления) страница может обновлять данные без перезагрузки, так что интерфейс также может контролировать URL-адрес для управлять собой, и рождается интерфейсная маршрутизация.

Реализация одностраничного приложения обусловлена ​​концепцией внешней маршрутизации.

2. Два принципа реализации интерфейсной маршрутизации

1 Маршрутизация хэшей

Мы часто видим # в URL-адресе, этот # имеет два случая, один из которых мы называем привязкой, например, типичный принцип кнопки «назад к началу», переход между различными заголовками на Github и т. д. # в маршруте не называется Anchor, мы называем его хэшем, большинство систем маршрутизации крупных фреймворков реализовано с помощью хэша.

Нам нужно событие, которое запускается путем прослушивания изменений хэша - событие HashChange

Объект окна предоставляет событие onhashchange для отслеживания изменения хеш-значения.Как только хеш-значение в URL-адресе изменится, событие будет запущено.

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

window.addEventListener('hashchange', function () {
  <!--这里你可以写你需要的代码-->
});

2 История маршрутизации

API истории HTML5 — это метод расширения, добавленный к объекту глобальной истории браузера.

Сосредоточьтесь на двух новых API: history.pushState и history.replaceState.

Оба API получают три параметра:

объект состояния — объект JavaScript, связанный с новой записью в истории, созданной с помощью метода pushState(). Всякий раз, когда пользователь переходит к вновь созданному состоянию, запускается событие popstate, а свойство состояния объекта события содержит копию объекта состояния записи истории.

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

Адрес (URL) — адрес новой записи в истории. Браузер не будет загружать адрес после вызова метода pushState(), но может попытаться загрузить его позже, например, если пользователь перезапустит браузер. Новый URL-адрес не обязательно должен быть абсолютным путем; если это относительный путь, он будет основан на текущем URL-адресе; входящий URL-адрес и текущий URL-адрес должны иметь одно и то же происхождение, в противном случае pushState() выдаст ошибку исключение. Этот параметр является необязательным, если он не указан, это текущий URL-адрес документа.

Набираем в консоли

window.history.pushState(null, null, "https://www.baidu.com/?name=lvpangpang");

Вы можете увидеть изменение URL-адреса браузера

Примечание. Указанный здесь URL-адрес не поддерживает междоменное использование. Например, если вы введете приведенный выше код под доменным именем, отличным от Baidu.

Тем не менее, этот режим выбирается в Vue или React, и новая страница отправляется на Луну.

Причина в том, что URL в режиме истории является настоящим URL-адресом, сервер будет искать ресурс в пути файла URL, и если ресурс не найден, он вернет 404. Народный пункт состоит в том, что этот режим будет распознан сервером и будет обработан соответственно.

Для этой проблемы 404 у нас есть много решений.

A Настройка веб-пакета (среда разработки)

historyApiFallback:{
    index:'/index.html'//index.html为当前目录创建的template.html
}

B Настройте ngnix (производственная среда)

location /{
    root   /data/nginx/html;
    index  index.html index.htm;
    error_page 404 /index.html;
}

3. Демонстрация маршрутизации

Далее я объясню шаг за шагом, как написать интерфейсную маршрутизацию.

То есть процесс превращения наших знаний в навыки.

Мы также видели выше, что маршрутизация отображает различный контент или страницы на основе разных URL-адресов. Для внешней маршрутизации это отображение разного контента в соответствии с разными URL-адресами.

Итак, вот код ниже.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<div id="root">
  <a href="#/index">首页</a>
  <a href="#/list">列表</a>
</div>
<script>
const root = document.querySelector('#root');
window.onhashchange = function (e) {
  var hash = window.location.hash.substr(1);
  if(hash === '/index') {
    root.innerHTML = '这是index组件';
  }
  if (hash === '/list') {
    root.innerHTML = '这是list组件';
  }
}
</script>
</body>
</html>

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

4. Версия маршрутизации js

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

4.1 Конфигурация параметров чистилища

Здесь я имитирую конфигурацию роутинга в vue, react, и по умолчанию это массив объектов роутинга.

//路由配置
const routes = [{
  path: '/index',
  url: 'js/index.js'
}, {
  path: '/list',
  url: 'js/list.js'
}, {
  path: '/detail',
  url: 'js/detail.js'
}];
var router = new Router(routes);

Вы можете увидеть, очень ли похожа приведенная выше конфигурация маршрутизации на vue и react. Просто url здесь указывает на js файл вместо компонента (на самом деле компонент это тоже js файл, компонент содержит html, css, js , в конечном итоге будет скомпилирован в файл js)

4.1 Общая структура чистилища

function Router(opts = []) {
  
}
Router.prototype = {
  init: function () {
    
  },

  // 路由注册
  initRouter: function () {
   
  },

  // 解析url获取路径以及对应参数数组化
  getParamsUrl: function () {
    
  },

  // 路由处理
  urlChange: function () {
    
  },

  // 渲染视图(执行匹配到的js代码)
  render: function (currentHash) {
    
  },

  // 单个路由注册
  map: function (item) {
    
  },

  // 切换前
  beforeEach: function (callback) {
    
  },

  // 切换后
  afterEach: function (callback) {
    
  },

  // 路由异步懒加载js文件
  asyncFun: function (file, transition) {
    
  }

}

4.1 Внутренний анализ чистилища

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

Функция инициализации

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

init() {
    var oThis = this;

    // 注册路由
    this.initRouter();
    
    // 页面加载匹配路由
    window.addEventListener('load', function () {
      oThis.urlChange();
    });
    
    // 路由切换
    window.addEventListener('hashchange', function () {
      oThis.urlChange();
    });
}
    
}

B функция initRouter + функция карты

Функция регистрации маршрута заключается в сопоставлении параметров массива объекта маршрута при инициализации, например, маршруту /index соответствует /js/index.js.

// 路由注册
initRouter: function() {
    var opts = this.opts;
    opts.forEach((item, index) => {
      this.map(item);
    });
}

// 单个路由注册
map: function (item) {
    path = item.path.replace(/\s*/g, '');// 过滤空格
    this.routers[path] = {
      callback: (transition) => {
        return this.asyncFun(item.url, transition);
      }, // 回调
      fn: null // 缓存对应的js文件
    }
}

this.routers используется для хранения объектов маршрутизации, а функция обратного вызова для выполнения каждого маршрута заключается в загрузке соответствующего файла js.

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

C функция asyncFun

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

// 路由异步懒加载js文件
 asyncFun: function (file, transition) {
    // console.log(transition);
    var oThis = this,
      routers = this.routers;

    // 判断是否走缓存
    if (routers[transition.path].fn) {
      oThis.afterFun && oThis.afterFun(transition)
      routers[transition.path].fn(transition)
    } else {
      var _body = document.getElementsByTagName('body')[0];
      var scriptEle = document.createElement('script');
      scriptEle.type = 'text/javascript';
      scriptEle.src = file;
      scriptEle.async = true;
      SPA_RESOLVE_INIT = null;
      scriptEle.onload = function () {
        oThis.afterFun && oThis.afterFun(transition)
        routers[transition.path].fn = SPA_RESOLVE_INIT;
        routers[transition.path].fn(transition)
      }
      _body.appendChild(scriptEle);
    }
 }

Функция рендеринга D

Как видно из названия, основная функция этой функции — отрисовка страницы, а здесь — выполнение js-файла, соответствующего маршруту загрузки. Здесь выносится решение, если есть охранник маршрутизации, прогулка охраняется.

// 渲染视图(执行匹配到的js代码)
  render: function (currentHash) {
    var oThis = this;
    // 全局路由守护
    if (oThis.beforeFun) {
      oThis.beforeFun({
        to: {
          path: currentHash.path,
          query: currentHash.query
        },
        next: function () {
          // 执行目标路由对应的js代码(相当于是组件渲染)
          oThis.routers[currentHash.path].callback.call(oThis, currentHash)
        }
      });
    } else {
      oThis.routers[currentHash.path].callback.call(oThis, currentHash);
    }
  }

E перед каждой функцией

Функция защиты маршрутизации, с помощью которой вы можете выполнять такие действия, как оценка прав входа в систему, похожа ли она на глобальную защиту маршрутизации vue-router?

// 切换前
  beforeEach: function (callback) {
    if (Object.prototype.toString.call(callback) === '[object Function]') {
      this.beforeFun = callback;
    } else {
      console.trace('请传入函数类型的参数');
    }
  },

Что ж, основной код чистилища написан выше, а соответствующий эффект мы можем увидеть ниже.