Что такое маршрутизация?
Концепция маршрутизации впервые появилась в спине. Когда передние и задние стороны не были разделены, задняя часть контролировала маршрутизацию. Сервер получил запрос клиента, анализируя соответствующий путь URL и вернул соответствующую Страница / Ресурс.
Проще говоря, маршрутизация — это отображение разного контента или страниц в соответствии с разными URL-адресами.
Происхождение внешней маршрутизации
Давным-давно ~ Каждая операция обновления пользователя должна обновлять страницу, что сильно влияет на интерактивный опыт. Позже, чтобы решить эту проблему, появился Ajax (схема асинхронной загрузки), который значительно улучшил опыт. .
Хотя Ajax решает болевые точки взаимодействия с пользователем, переход между несколькими страницами также будет иметь негативные последствия, поэтому родилось использование spa (одностраничного приложения). Приложение spa реализовано на основе внешней маршрутизации, поэтому есть внешняя маршрутизация.
В настоящее время популярные vue-router/react-router также основаны на принципе внешней маршрутизации.
Два принципа реализации интерфейсной маршрутизации
1. Режим хеширования
Объект окна предоставляет событие onhashchange для отслеживания изменения хеш-значения.Как только хеш-значение в URL-адресе изменится, событие будет запущено.
window.onhashchange = function(){
// hash 值改变
// do you want
}
2.Режим истории
API истории HTML5 — это метод расширения, добавленный к объекту глобальной истории браузера.
Проще говоря, история на самом деле является интерфейсом к стеку истории браузера. Я не буду здесь подробно останавливаться на каждом API истории. Подробности можно узнатьпортал
Объект окна предоставляет событие onpopstate для отслеживания изменений в стеке истории.Как только информация в стеке истории изменится, это событие будет запущено.
Важно отметить, что вызов history.pushState() или history.replaceState() не вызовет событие popstate. Это событие запускается только при выполнении действия браузера.
window.onpopstate = function(){
// 历史栈 信息改变
// do you want
}
history предоставляет два API для управления стеком истории: history.pushState и history.replaceState.
history.pushState(data[,title][,url]);//向历史记录中追加一条记录
history.replaceState(data[,title][,url]);//替换当前页在历史记录中的信息。
// data: 一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
//title: FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。
//url: 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。
Сравнение плюсов и минусов двух режимов
В сравнении | Hash | History |
---|---|---|
декоративный | уродливый | Ницца |
совместимость | >ie8 | >ie10 |
практичность | Использовать напрямую | Нужна внутренняя кооперация |
Пространства имен | тот же документ | гомология |
Создать (cao) простой интерфейсный маршрут
Эта демонстрация просто хочет помочь нам лучше понять концепцию внешней маршрутизации на практике, поэтому выполняется только простая реализация ~
404 в режиме истории
Когда мы используем режим истории, если он не настроен, при обновлении страницы будет отображаться 404.
Причина в том, что URL-адрес в режиме истории является реальным URL-адресом, сервер будет искать ресурс в пути к файлу URL-адреса, и если ресурс не будет найден, он вернет 404.
Решение этой проблемы здесь не рассматривается.Погуглите, и вы узнаете~ Мы используем атрибут historyApiFallback в webpack-dev-server в следующей демонстрации для поддержки режима истории HTML5.
файловая структура
|-- package.json
|-- webpack.config.js
|-- index.html
|-- src
|-- index.js
|-- routeList.js
|-- base.js
|-- hash.js
|-- history.js
1. Создайте окружение
Не говори глупостей, просто иди к коду~
package.json
{
"name": "web_router",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --config ./webpack.config.js"
},
"author": "webfansplz",
"license": "MIT",
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.28.1",
"webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.1.14"
}
}
webpack.config.js
'use strict';
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: '[name].js'
},
devServer: {
clientLogLevel: 'warning',
hot: true,
inline: true,
open: true,
//在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html (解决histroy mode 404)
historyApiFallback: true,
host: 'localhost',
port: '6789',
compress: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
})
]
};
2. Откройтесь
Прежде всего, мы сначала определяем функции и параметры конфигурации, которые нам необходимо реализовать.
Внешняя маршрутизация | параметр | метод |
---|---|---|
- | Режим | толкать |
- | Список маршрутов (routeList) | заменить (заменить) |
- | - | идти (вперед/назад) |
src/index.js
const MODE='';
const ROUTELIST=[];
class WebRouter {
constructor() {
}
push(path) {
...
}
replace(path) {
...
}
go(num) {
...
}
}
new WebRouter({
mode: MODE,
routeList: ROUTELIST
});
Ранее мы говорили, что существует два способа реализации внешней маршрутизации.
1. Определите список маршрутизации
2. Мы создаем соответствующие классы для этих двух методов соответственно и создаем их экземпляры в соответствии с различными параметрами режима, чтобы завершить реализацию класса webRouter.
src/routeList.js
export const ROUTELIST = [
{
path: '/',
name: 'index',
component: 'This is index page'
},
{
path: '/hash',
name: 'hash',
component: 'This is hash page'
},
{
path: '/history',
name: 'history',
component: 'This is history page'
},
{
path: '*',
name: 'notFound',
component: '404 NOT FOUND'
}
];
src/hash.js
export class HashRouter{
}
src/history.js
export class HistoryRouter{
}
src/index.js
import { HashRouter } from './hash';
import { HistoryRouter } from './history';
import { ROUTELIST } from './routeList';
//路由模式
const MODE = 'hash';
class WebRouter {
constructor({ mode = 'hash', routeList }) {
this.router = mode === 'hash' ? new HashRouter(routeList) : new HistoryRouter(routeList);
}
push(path) {
this.router.push(path);
}
replace(path) {
this.router.replace(path);
}
go(num) {
this.router.go(num);
}
}
const webRouter = new WebRouter({
mode: MODE,
routeList: ROUTELIST
});
Мы реализовали функцию webRouter ранее, а затем реализуем двумя способами.
Поскольку оба режима должны вызывать метод для обновления содержимого разных маршрутов, поэтому~
index.html
<!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>前端路由</title>
</head>
<body>
<div id="page"></div>
</body>
</html>
js/base.js
const ELEMENT = document.querySelector('#page');
export class BaseRouter {
//list = 路由列表
constructor(list) {
this.list = list;
}
render(state) {
//匹配当前的路由,匹配不到则使用404配置内容 并渲染~
let ele = this.list.find(ele => ele.path === state);
ele = ele ? ele : this.list.find(ele => ele.path === '*');
ELEMENT.innerText = ele.component;
}
}
хорошо, давайте реализуем два режима.
Хэш-режим
src/hash.js
import { BaseRouter } from './base.js';
export class HashRouter extends BaseRouter {
constructor(list) {
super(list);
this.handler();
//监听hash变化事件,hash变化重新渲染
window.addEventListener('hashchange', e => {
this.handler();
});
}
//渲染
handler() {
this.render(this.getState());
}
//获取当前hash
getState() {
const hash = window.location.hash;
return hash ? hash.slice(1) : '/';
}
//获取完整url
getUrl(path) {
const href = window.location.href;
const i = href.indexOf('#');
const base = i >= 0 ? href.slice(0, i) : href;
return `${base}#${path}`;
}
//改变hash值 实现压入 功能
push(path) {
window.location.hash = path;
}
//使用location.replace实现替换 功能
replace(path) {
window.location.replace(this.getUrl(path));
}
//这里使用history模式的go方法进行模拟 前进/后退 功能
go(n) {
window.history.go(n);
}
}
Режим истории
src/history.js
import { BaseRouter } from './base.js';
export class HistoryRouter extends BaseRouter {
constructor(list) {
super(list);
this.handler();
//监听历史栈信息变化,变化时重新渲染
window.addEventListener('popstate', e => {
this.handler();
});
}
//渲染
handler() {
this.render(this.getState());
}
//获取路由路径
getState() {
const path = window.location.pathname;
return path ? path : '/';
}
//使用pushState方法实现压入功能
//PushState不会触发popstate事件,所以需要手动调用渲染函数
push(path) {
history.pushState(null, null, path);
this.handler();
}
//使用replaceState实现替换功能
//replaceState不会触发popstate事件,所以需要手动调用渲染函数
replace(path) {
history.replaceState(null, null, path);
this.handler();
}
go(n) {
window.history.go(n);
}
}
3. Готово
Таким образом, простая внешняя маршрутизация завершает получение.
Если вы думаете, что это поможет вам, дайте звезду~