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('请传入函数类型的参数');
}
},
Что ж, основной код чистилища написан выше, а соответствующий эффект мы можем увидеть ниже.