前端路由的基本原理

813 阅读3分钟

前言

目前前端开发最流行的莫过于SPA单页面架构,前端开发的三驾马车reactvueangluarJS均是基于此模型来运行的。单页面指的是只有一个主页面,通过动态的替换DOM内容并修改URL地址,来模拟多页面的效果,切换页面的功能直接由前台脚本完成。SPA能够模拟多页面应用的效果,归功于前端路由机制。它有两种实现方式

1. HashChange

基本原理

更改URL的hash值(锚点),然后触发window对象的hashChange事件,通过代码触发对应页面的DOM改变,就可以实现基本的路由了

实例

    // HTML 
    <a href="#angluarJS">angluarJs</a>
    <a href="#react">react</a>
    <a href="#vue">vue</a>
    <div id="content"></div>

    // JS
    location.hash = 'angluarJs';
    let c = document.querySelector('#content');
    window.onhashchange = function (opt) {
        c.innerHTML = '当前页面是' + location.hash;
    }

2. History API

在HTML4中,已经支持window.history对象来控制页面历史记录跳转,常用方法包括:

  1. history.forward(); 相当于浏览器的前进
  2. history.back(); 相当于浏览器的后退
  3. history.go(n); 在历史记录中跳转n步骤,n=0时刷新页面,n=1时后退一页

在HTML5中,window.history再次扩展,新增API有:

  1. history.pushState(); 向历史记录中追加一条记录
  2. history.replaceState(); 替换当前页在历史记录中的信息
  3. history.state; 一个属性,当前页的state信息
  4. window.onpopstate(); 一个事件,当点击浏览器前进后退的时候触发

基本原理

浏览器访问一个页面的时候,当前地址的信息会被压入历史栈,当调用history.pushState()方法向历史栈中压入一个新的state后,历史栈顶部的指针就指向了这个新的state,就相当于假装已经修改了URL地址并进行跳转,当点击浏览器的前进后退或者JS触发前进后退时,触发window.onpopstate事件。

实例

    // HTML
    <h1>前端三驾马车</h1>
    <ul id="list">
        <li>angularJs</li>
        <li>react</li>
        <li>vue</li>
    </ul>
    <div id="content"></div>

    // JS
    document.querySelector('#list').addEventListener('click',function (e) {
        if(e.target.tagName === 'LI') {
            let content = e.target.innerHTML;
            let newState = {
                url: location.origin + '/' + content,
                title: document.title,
                state: content
            };
            // (data,title,url)
            window.history.pushState(newState,'','/'+content);
            urlChange(newState);
        }
    },false);
    // 浏览器前进后退的时候触发
    window.onpopstate = function (e) {
        // pushState或repalceState方法传入的data参数
        urlChange(e.state);
    };
    let c = document.querySelector('#content');
    function urlChange(state) {
        if(state) {
            c.innerHTML = '当前页是' + state.state;
        }else {
            c.innerHTML = '当前页是angularJs';
        }
    }

两种方式比较

对比 hash路由 History API路由
命名限制 通常只能在一个document下进行改变 URL地址可以自定义,只要同一个域名下就可以
URL地址变更 会改变 可以改变,也可以不改变
状态保存 无内置方法 将页面压入信息栈时可以附带自定义的信息
实用性 可直接使用 需要服务器修改代码配合实现
兼容性 IE8以上 IE10以上

应用

我封装了一个插件,可实现简单的前端路由,支持hash和history模式,代码在这里,使用如下:

hash模式

    // HTML
    <p>前端三驾马车</p>
    <ul>
        <li><a ym-link="#angularJs" >angular</a></li>
        <li><a ym-link="#vue">vue</a></li>
        <li><a ym-link="#react">react</a></li>
    </ul>
    <div id="ymapp">这里是模板容器</div>
    
    // JS
    $YMRouter.init({
        mode: 'hash',
        list: {
            '#angularJs': 'this is the page of angularJs',
            '#vue': 'this is the page of vue',
            '#react': 'this is the page of react'
        }
    });

history模式

    // HTML
    <p>前端三驾马车</p>
    <ul>
        li><a ym-link="/angularJs" >angular</a></li>
        <li><a ym-link="/vue">vue</a></li>
        <li><a ym-link="/react">react</a></li>
    </ul>
    <div id="ymapp">这里是模板容器</div>
    
    // JS
    $YMRouter.init({
        mode: 'hash',
        list: {
            '/angularJs': 'this is the page of angularJs',
            '/vue': 'this is the page of vue',
            '/react': 'this is the page of react'
        }
    });

问题

  1. 服务器端必须做路由处理,无论前端访问哪个页面,都返回首页,不然刷新页面会出现404
  2. history模式也可以不改变URL,但是本插件未做此功能