前端路由概述

776 阅读4分钟

不论学什么知识,我们都应该先去学那些最基础的东西,前端路由也是一样,不论是 vue-router 还是 react-router ,其实核心的东西都是一样的,只是各自的 api 和特点处理得不同,那么首先我们应该了解路由是什么?

学过计算机网络的都知道路由器、路由表,网络中的路由就是指在互联的网络上将信息从源地址传送到目的地址的过程,路由器的作用就是引导分组转发,转发的时候则按照路由表中指定的地址,路由表上则存储了到各个目的地的最佳路径。那么在前端中的路由指的是什么呢?

其实路由的概念都一样,但是具体的用法和实现不一样,当我们在实现页面跳转、内容切换的时候往往就会用到路由,例如当我们从当前页面跳转到另一个页面的时候,输入的 url 就会发生改变,url 就表明了我们的目的地址,监听 url 改变后就去查询定义好的路由表,然后实现页面内容切换。

在路由的概念中,有三个重要的点:目的地址、路由表、信息,所以我们在实现路由的时候,也要特别关注这三个点。在前端路由中,有三个方式实现路由,分别是 hash 模式、history 模式、memory 模式。下面分别阐述:

hash 模式

hash 模式是我们比较常用的模式,它的核心概念就是监听 url 中的 hash 变化来实现页面跳转,也就是 url 中 # 后面的内容。如下代码(注意默认路由和保底路由的设置。默认路由:# 后没有内容时跳转的页面;保底路由:路径输入错误时的路由):

// index.js
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);
});

// index.html
<body>
  <a href="#1">go to 1</a>
  <a href="#2">go to 2</a> 
  <a href="#3">go to 3</a>
  <a href="#4">go to 4</a>
  <div id="app"></div>
  <div id="div404" style="display: none;">404页面</div>
  <script src="src/index.js"></script>
</body>

history 模式

history 模式的核心概念是利用 html5 新出的 history 对象来实现路由跳转,关于 history 的更多细节,可查询 mdn 文档,实现的过程主要是判断 url 中 / 后面的内容以及运用 history.pushState 方法,实现代码如下:

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.pathname;
  console.log("number: " + number);
  if (number === "/") {
    number = "/1";
  }
  // 获取界面
  let div = routeTable[number.toString()];
  if (!div) {
    div = document.querySelector("#div404");
  }
  div.style.display = "block";
  // 展示界面
  container.innerHTML = "";
  container.appendChild(div);
}
const allA = document.querySelectorAll("a.link");
for (let a of allA) {
  a.addEventListener("click", e => {
    e.preventDefault();
    const href = a.getAttribute("href");
    window.history.pushState(null, `page ${href}`, href);
    // 通知
    onStateChange(href);
  });
}
route(app);
function onStateChange() {
  console.log("state 变了");
  route(app);
}

// index.html
<body>
  <a href="#1">go to 1</a>
  <a href="#2">go to 2</a> 
  <a href="#3">go to 3</a>
  <a href="#4">go to 4</a>
  <div id="app"></div>
  <div id="div404" style="display: none;">404页面</div>
  <script src="src/index.js"></script>
</body>

memory 模式

memory 模式貌似不是很常用,它的核心概念并不是依据 url 来判断,而是将跳转的路径放在用户看不见的地方,例如放在 localStorage 中,如下代码所示:

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.localStorage.getItem("xxx");
  if (!number) {
    number = "/1";
  }
  // 获取界面
  let div = routeTable[number.toString()];
  if (!div) {
    div = document.querySelector("#div404");
  }
  div.style.display = "block";
  // 展示界面
  container.innerHTML = "";
  container.appendChild(div);
}
const allA = document.querySelectorAll("a.link");
for (let a of allA) {
  a.addEventListener("click", e => {
    e.preventDefault();
    const href = a.getAttribute("href");
    window.localStorage.setItem("xxx", href);
    // 通知
    onStateChange(href);
  });
}
route(app);
function onStateChange() {
  console.log("state 变了");
  route(app);
}

// index.html
<body>
  <a href="#1">go to 1</a>
  <a href="#2">go to 2</a> 
  <a href="#3">go to 3</a>
  <a href="#4">go to 4</a>
  <div id="app"></div>
  <div id="div404" style="display: none;">404页面</div>
  <script src="src/index.js"></script>
</body>

三者比较

简要地说,hash 模式是依据 url 中 # 后面的内容判断路径,history 模式是依据 url 中 / 后面的内容判断路径,memory 模式则一般是依据本地存储来存储路径,三种模式的优缺点如下:

hash 模式:适用于任何情况,但是 SEO 不友好,因为路径的判断是根据 url 中 # 后的内容,而 # 后的内容并不会跟随 url 发送到服务端,浏览器会将 # 后的内容自动丢弃,于是服务器就只会响应默认路由,搜索引擎也收录不到。

history 模式:IE8 以下不支持,同时只适用于后端能够将所有前端路由都渲染为同一个页面(不是404页面),因为该模式是依据 url 中 / 后面的内容判断路径,/ 后面的内容会发送到服务器上,虽然可以阻止提交到服务器,但是一旦用户刷新的话就会造成路径不存在的情况。

memory 模式:适用于单机版的路由。