Vue Router - 前端路由实现思路

189 阅读7分钟

什么是路由

什么是路由器?一个及以上的人在家里要上网,就要连路由器,路由器连电信,电信卖IP端口,百度,抖音就购买端口。当你在手机上打开抖音,这个请求就由路由器分发出去到抖音,然后把你要的信息传回来。路由主要就是分发请求的,路由器就是用来分发请求的东西

image.png

什么是前端路由

前端路由的诞生的缘由前端路由的出现要从 ajax 开始,Ajax,全称 Asynchronous JavaScript And XML,是浏览器用来实现异步加载的一种技术 方案。

在 90s 年代初,大多数的网页都是通过直接返回 HTML 的,用户 的每次更新操作都需要重新刷新页面。极其影响交互体验,随着网络的发展,迫切需要一种方案来改善这种情况。

1996,微软首先提出 iframe 标 签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest 的前身), 并在 IE5 通过 ActiveX 来实现了这项技术。在微软实现这个概念后,其他 浏览器比如 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。 ( 兼容问题从此出现)不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。

有了 Ajax 后,用户交互就不用每次都刷新页面, 体验带来了极大的提升。但真正让这项技术发扬光大的,还是后来的Google Map,它的出现向人们展现了 Ajax 的真正魅力,释放了众多开发人员的想象力,让其不仅仅局限于简单的数据和页面交互,为后来异步交 互体验方式的繁荣发展带来了根基。而异步交互体验的更高级版本就是 SPA —— 单页应用。

单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。单页应用的概念是伴随着 MVVM 出现的。最早由微软提出,然后他们在浏览器端用 Knockoutjs 实现。但 这项技术的强大之处并未当时的开发者体会到,可能是因为 Knockoutjs 实现过于复杂,导致没有大面积的扩散。同样,这次接力的选手依然是 Google。Google 通过 Angularjs 将 MVVM 及单页应用发扬光大,让前端开发者能够开发出更加大型的应用,职能变得更大了随后都是就是前端圈开始得到了爆发式的发展,陆续出现了很多优秀的框架。

前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,之 前是通过服务端根据 url 不同返回不同的页面来实现。

image.png

//.html
<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <a class="link" href="/1">go to 1</a>
    <a class="link" href="/2">go to 2</a>
    <a class="link" href="/3">go to 3</a>
    <a class="link" href="/4">go to 4</a>
    <div id="app"></div>

    <div id="div404" style="display: none;">你要找的内容被狗吃了</div>

    <script src="src/index.js"></script>
  </body>
</html>

image.png

hashchange

当URL中的片段标识符发生改变时触发(URL中紧跟'#'号的部分,包括‘#’号)

默认路由

默认路由(Default route),是对IP数据包中的目的地址找不到存在的其他路由时,路由器所选择的路由。目的地不在路由器的路由表里的所有数据包都会使用默认路由。这条路由一般会连去另一个路由器,而这个路由器也同样处理数据包: 如果知道应该怎么路由这个数据包,则数据包会被转发到已知的路由;否则,数据包会被转发到默认路由,从而到达另一个路由器。每次转发,路由都增加了一跳的距离。

在上例中,默认路由为

number = number || 1

404/保底路由

if(div){
    div.style.display = 'block';
}else{
    div = document.querySelector('#div404');
    div.style.display = 'block';
}

路由表

image.png

const app = document.querySelector("#app");
const div1 = document.createElement("div");
div1.innerHTML = "1";
const div2 = document.createElement("div");
div2.innerHTML = "2";
const div3 = document.createElement("div");
div3.innerHTML = "3";
const div4 = document.createElement("div");
div4.innerHTML = "4";
const routeTable = {
  "1": div1,
  "2": div2,
  "3": div3,
  "4": div4
};

function route(container) {
  let number = window.location.hash.substr(1);

  number = number || 1;

  // 获取界面
  let div = routeTable[number.toString()];
  if (!div) {
    div = document.querySelector("#div404");
  }
  div.style.display = "block";

  // 展示界面
  container.innerHTML = "";
  container.appendChild(div);
}

route(app);

window.addEventListener("hashchange", () => {
  console.log("hash 变了");
  route(app);
});

嵌套路由

div1里面还有div1.1、1.2、1.3等,具体实现方法有点复杂,请自行百度

image.png

hash模式-let number = window.location.hash.substr(1);

任何情况下都可以用hash做前端路由。

早期的前端路由的实现就是基于location.hash来实现的。其实现原理也很简单,location.hash的值就是URL中#后面的内容。比如下面这个网站,它的location.hash='#me'

https://www.srtian.com#me

此外,hash也存在下面几个特性:

URL中hash值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash部分不会被发送。

hash值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash的切换。

我们可以使用hashchange事件来监听hash的变化。

触发hsah变化的方式也有两种,一种是通过a标签,并设置href属性,当用户点击这个标签后,URL就会发生改变,也就会触发hashchange事件了:

<a href="#srtian">srtian</a>

还有一种方式就是直接使用JavaScript来对loaction.hash进行赋值,从而改变URL,触发hashchange事件:

location.hash="#srtian"

让我们来整理思路,假如我们要用 hash 的模式实现一个路由,那么流程应该是这样的。

image.png

缺点-SEO不友好

主要原因:服务器收不到hash

Google有针对hash的SEO,#!(hashbang),如果在#后发现!,那么Google就会认为是不同页面,但还是不能和以前的SEO相媲美,基本没什么用。

history模式-let number = window.location.pathname;

后端将所有前端路由渲染到同一个页面时,可以使用history模式。 vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

当你使用 history 模式时,URL 就像正常的 url,例如

http://yoursite.com/user/id

也好看!

不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

缺点-IE8以下不支持

memory模式-let number = window.localStorage.getItem("xxx");

前边两种模式都是通过不同的url得到结果。memory模式是把路径存到一个用户看不见的地方,比如localStorage。而搜索的路径url是不变的,通过从存储库里取出不同的路径来实现路由。

memory模式适合非浏览器路由,比如app里的路由,它不是网页,是没有路径的。所以就只能用memory模式。再比如react native,也是没有路径的。

总结

hashhistory 都是把路径存在url上的,memory不用url。memory的缺点就是没有url,因为路径没有放到url上,而是存在本地的存储库里,所以这是单机版路由,我分享出去这个网址,由于你那里没存路径信息,所以维持初始的路由。

Vue-router源码