[Практическая серия] Маршрутизация внешнего интерфейса

внешний интерфейс
[Практическая серия] Маршрутизация внешнего интерфейса

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

Концепция маршрутизации впервые появилась в спине. Когда передние и задние стороны не были разделены, задняя часть контролировала маршрутизацию. Сервер получил запрос клиента, анализируя соответствующий путь 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. Готово

Таким образом, простая внешняя маршрутизация завершает получение.

Адрес источника

Если вы думаете, что это поможет вам, дайте звезду~