[实践系列]-前端路由

什么是路由?

路由这观点最最先是在后端涌现的,在之前前后端不星散的时刻,由后端来掌握路由,服务器吸收客户端的要求,剖析对应的url途径,并返回对应的页面/资本。

简朴的说 路由就是依据差别的url地点来展现差别的内容或页面.

前端路由的泉源

在良久良久之前~ 用户的每次更新操纵都须要从新革新页面,非常的影响交互体验,厥后,为了处理这个题目,便有了Ajax(异步加载计划),Ajax给体验带来了极大的提拔。

虽然Ajax处理了用户交互时体验的痛点,然则多页面之间的跳转一样会有不好的体验,所以便有了spa(single-page application)运用的降生。而spa运用就是基于前端路由完成的,所以便有了前端路由。

现在比较火的vue-router/react-router 也是基于前端路由的道理完成的~

前端路由的两种完成道理

1.Hash形式

window对象供应了onhashchange事宜来监听hash值的转变,一旦url中的hash值发作转变,便会触发该事宜。

window.onhashchange = function(){
    
    // hash 值转变 
    
    // do you want
}

2.History 形式

HTML5的History API 为浏览器的全局history对象增添的扩大要领。

简朴来讲,history实在就是浏览器汗青栈的一个接口。这里不细说history的每一个API啦。详细可查阅 传送门

window对象供应了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。

两种形式好坏对照

对照HashHistory
观赏性
兼容性>ie8>ie10
实用性直接运用需后端合营
定名空间统一document同源

造(cao) 一个简朴的前端路由

本demo只是想说协助我们经由过程实践更进一步的明白前端路由这个观点,所以只做了简朴的完成~

history形式404

当我们运用history形式时,假如没有举行设置,革新页面会涌现404。

缘由是由于history形式的url是实在的url,服务器会对url的文件途径举行资本查找,找不到资本就会返回404。

这个题目的处理计划这里就不细说了,google一下,你就晓得~ 我们在以下demo运用webpack-dev-server的里的historyApiFallback属性来支撑HTML5 History Mode。

文件构造

|-- 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.开撸

起首我们先初始化定义我们须要完成的功用及设置参数。

前端路由参数要领
x形式(mode)push(压入)
x路由列表(routeList)replace(替代)
xxgo(行进/退却)

src/index.js


const MODE='';

const ROUTELIST=[];

class WebRouter {
  constructor() {
    
  }
  push(path) {
  
   ...
  }
  replace(path) {
  
   ...
    
  }
  go(num) {
  
   ...
    
  }
}

new WebRouter({
  mode: MODE,
  routeList: ROUTELIST
});

前面我们说了前端路由有两种完成体式格局。

1.定义路由列表

2.我们分别为这两种体式格局建立对应的类,并依据差别的mode参数举行实例化,完成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的功用,接下来我们来完成两种体式格局。

由于两种形式都须要挪用一个要领来完成差别路由内容的革新,so~

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;
  }
}

ok,下面我们来完成两种形式。

Hash形式

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);
  }
}

History形式

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.小功乐成

就这样,一个简朴的前端路由就完成拉。

源码地点

假如以为有协助到你的话,给个star哈~

    原文作者:null仔
    原文地址: https://segmentfault.com/a/1190000017853553
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞