浅说vue-router原理

101 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

vue-router 用的多了吧,就是路由,可是经常用,知道为什么定义了几个路由地址,就可以根据路由来切换页面吗,毕竟我们用的单页面应用和之前有所有不同,以前的前端就是各种的a标签+href属性写上地址就跳转,那我们一个个路由改变时候页面就改变是为什么,还有有hash和history两种方法又有什么区别?略略了解过一点点呢,浅浅的分享一下

hash路由介绍

eg: https://test.com/#/a-page

  • url有个#符号,但是#只是客户端得状态,实不会传来服务器(后端是获取不到的哟)
  • hash得更改,不会导致页面更新
  • hash得更改,会在浏览器的访问历史中添加一条记录,所以我们可以通过浏览器得返回前端按钮控之hash得切换
  • hash得更改,会触发 hashchange 事件(这个很重要,核心实现)
// 监听hash值变化
window.addEventListener('hashchange', () => {});
// 通过改变hash值来触发(在vue里面也就是我们所说的路由跳转)
location.hash = '#aaa';
location.hash = '#bbb';

乞丐版hash路由实现

先看看代码:

class Router {
  constructor() {
    this.routes = {}; // 初始化路由对象,拿来装载路由的
    // 最核心的,就是通过#的改变,就调用更新方法
    window.addEventListener('hashchange', () => {
      // 当hash值变动的时候改变,
      this.refresh()
    });
  }
  // 添加路由进来
  route(path, callback) {
    this.routes[path] = callback || function() {};
  }
  // 这个调用就是获取现在的在地址hash值,拼接成我们添加时候的路由路径
  // #red => /red,这个就是我们刚刚添加的路由
  refresh() {
    const hashUrl = location.hash.substr(1)};
    this.routes[hashUrl]();
  }
}
const router = new Router();
// 注册 /red 路由
router.route('/red', function() {
    document.querySelector('body').style.background = 'red';
})
// 注册 /orange 路由
router.route('/orange', function() {
    document.querySelector('body').style.background = 'orange';
})
<div class="box">
   <a href='#/red'>红</a>
   <a href='#/orange'>橙色</a>
</div>

分析一下实现

  • 首先我们要知道最核心的是hashchange这个事件,因为他是可以监听hash值的变化,我们监听到hash值的变化我们才知道路由变化了,我们才能去拿到当前的hash然后做对应hash值要去的页面
  • 我们有了可以监听hash值变化的东西,然后我们拿到hash值怎么知道要做什么? (实际就是要去哪个页面)那么我们就有个方法refresh里面写的就是,获取当前的的hash值,然后调用hash值对应的方法
  • 那么hash值对应的方法怎么获取,通过一个对象routes 属性名就是路由,属性值就是对应的函数,routes对象实际是这样的:
// 属性名是 路由=》/red
// 属性值是 方法=〉() => {这里写干什么}
// 里面是各种路由对象
const routes = {
  '/red': () => {},
  '/orange:': () => {},
  '/page-one:': () => {},
}

当我们hash值变化时候,拿到当前hash值也就是 /red这种,我们就会直接调用routes[hash值]()对应的函数

  • 那我们routes那么多的属性哪里来的,那就route函数,它专门用来注册路由的,接收两个参数,一个是路由路径值path,一个是对应函数callback,函数里面就是一句代码this.routes[path]=callback;

实际效果就是这样:

history路由介绍

eg: https://test.com/a-page

基本使用

  • window.history.pushState()
  • window.history.replaceState()

都接受三个参数,一个是state,这个是触发popState函数(监控地址变化的方法,待会说)时候可以获取到的参数,一个是title(这个用处不大,当前基本所有浏览器都忽略了这个),一个是url设置当前的路由的;但是注意这里的pushState和replaceState都不会触发popState的监听

  • window.history.back(); 后退
  • window.history.forward(); 前进
  • window.history.go(-1); 接收number,后退1页

特点

  • 没有#
  • pushState/replaceState 并不会触发 popstate 事件
  • 可以用popstate来监听url的变化
  • popstate到底什么才能触发
    • 点击浏览器前进/后退按钮
    • 调用back/forward/go方法

乞丐版history路由实现

其实和hash的实现很类似,我们先上代码

class Router {
    constructor() {
        this.routes = {};
        this.push(location.pathname);
        this._bindPopState()

    }

    push(path) {
        window.history.pushState({path}, null, path)
        const cb = this.routes[path];
        cb && cb();
    }

    replace(path) {
        window.history.replaceState({path}, null, path);
        const cb = this.routes[path];
        cb && cb();
    }

    route(path, callback) {
        this.routes[path] = callback || function () {}
    }

    _bindPopState() { // 为了后退时候监控,就是 window.history.go(1)/back/forward
        window.addEventListener('popstate', (e) => {
            const path = e.state && e.state.path;
            this.routes[path] && this.routes[path]();
        })
    }
}
const router = new Router();
 router.route('/', function() {
     document.querySelector('body').style.background = 'white';
 })
 router.route('/orange', function() {
     document.querySelector('body').style.background = 'orange';
 })
 router.route('/red', function() {
     document.querySelector('body').style.background = 'red';
 })
<div class="box">
    <a href='/'>白色</a>
    <a href='/orange'>橙色</a>
    <a href='/red'>红色</a>
</div>
<script>
  // 这段代码主要就是让我们点击的时候使用我们的push方法去做的跳转,
  // 而不是真的用href去跳,不然就请求新的页面了
  document.querySelector('body').addEventListener('click', (e) => {
        if (e.target.tagName === 'A') {
            e.preventDefault();
            router.push(e.target.getAttribute('href'));
        }
    })
</script>

分析一下实现

其实前面几个步骤基本和上面hash的实现一样,只不过监听的函数换成了popstate

  • 先实现一个类Router,里面有routes属性装载路由
  • 使用popstate监控路由变化然后获取到路由字符,然后调用路由对应的方法
  • 路由就是由route方法添加进去routes

那这么说,一摸一样了?有点点不同,就是如果我们要使用pushState和replaceState来更改url的时候,我们需要新写两个方法

  • 一个push方法对应pushState
  • 一个replace对应replaceState

当调用了这个方法以后我们就要手动的调用一下你目标跳转路由对应的方法,因为这里是不会触发popstate,也就是我们写的_bindPopState方法。

好了,结束结束,继续加班!!!