关于前端路由

276 阅读4分钟

关于前端路由

首先什么是路由?

路由就是通过互联的网络把信息从源地址传输到目的地址的活动。这些活动的传送端和接收端也许是比较复杂不止一个的,而路由则引导两端传输如何进行,经过一些中间的节点后,到它们最后的目的地,这个过程我们称作 “分发请求”。路由有很多种形式:单播,多播,任播,广播及地域性广播。

而我们平常使用的路由器则是具有路由功能的硬件设备。

前端路由及一些简单概念

  • 前端路由:根据不同的url或者其他的标识内容,请求各自对应的内容并展现出来。
  • 路由表:存储路由引导规则的哈希表。
  • hash:包含块标识符(#)的DOMString,开头有一个“#”。
  • 默认路由:当路径中没有特定的路由标识时,默认导向的路由。
  • 404路由:又称:保底路由,当路径中的路由在路由表中没有匹配的地址时跳转的路由。
  • 嵌套路由:2层及以上的路由。

前端路由的三种模式

  1. hash模式

    1. 概念:以地址(url)中的hash作为路由识别标识的前端路由模式。

    2. 优点:

      1. 兼容性好,达到了ie8,且绝大多数框架都支持,所以绝大多数情况下都能够使用hash模式。
      2. 利用a标签跳转锚点,除了发送Ajax及资源加载的请求外,不会发送别的请求。
      3. 不需要再服务端进行设置。
    3. 缺点:

      • URL丑

      • SEO不友好:

        因为服务器接收不到hash。比如,在输入 www.baidu.com/#123 ,在控制台Network下,我们可以发现,我们发送请求的URL 还是 www.baidu.com 所以百度的服务器是接收不到hash的。这就导致N个不同的路由 最后都是导向了 www.baidu.com 。搜索引擎就认为这N个路由都是www.baidu.com这个页面,所以最后只会展示www.baidu.com的页面,而其他路由则搜索不出来。

        不过谷歌搜索引擎针对hash模式新出了一个功能——hashbang,能够识别hash,不过要在#后面加个!如:#!123。

  2. history模式(browser模式)

    1. 概念:由于实现过程中使用了HTML5中的history的api,所以称作:history模式。
    2. 优点:
      1. 正常的URL,比较容易处理。
      2. 绝大数框架也支持history模式。
      3. 后端能够准确追踪到路由。
    3. 缺点:
      1. 兼容性不如hash,只兼容到了ie10
      2. 需要后端的支持,所有的前端路由都要跳转到同一个页面,一般来说是index.html,然后根据JS中的逻辑以及url中的pathname进行页面渲染。 如果不这样做直接输入某路由的地址将会出现404报错。
  3. memory模式

    概念:在内存中模拟一个堆栈来管理历史记录。

    这个模式已经很少使用,适用的情况很少,一般只会应用于app。

    而且由于不同终端上的记录存储不同,会导致内容无法实现分享,该模式又称作:“单机路由”。

实现一个简单hash的路由

<!-- html -->
<body>
    <!-- 使用的是锚点,不会发生跳转 -->
    <a href="#1" class='link'>one</a>
    <a href="#2" class='link'>two</a>
    <a href="#3" class='link'>three</a>
    <a href="#4" class='link'>four</a>
    <div id="app"></div>
    
    <script src="./index.js"></script>
</body>
// js
// 需要展示的各个dom
const div1 = document.createElement('div')
div1.innerHTML = 'one'
const div2 = document.createElement('div')
div2.innerHTML = 'two'
const div3 = document.createElement('div')
div3.innerHTML = 'three'
const div4 = document.createElement('div')
div4.innerHTML = 'four'

// 挂载容器
const container = document.querySelector('#app')

// 路由表
const routeMap = {
    '1': div1,
    '2': div2,
    '3': div3,
    '4': div4,
}

route()

// 监听hash的变化
window.addEventListener('hashchange', () => {
    route()
})

function route() {
    let number = window.location.hash.substr(1) // 获取hash值并处理
    number = number || '1' // 默认路由
    container.innerHTML = '' 
    if (routeMap[number]) {
        container.appendChild(routeMap[number])
    } else {
        // 404路由
        const notFound = document.createElement('div')
        notFound.innerHTML = 'notound'
        container.appendChild(notFound)
    }
}

实现一个简单的history路由

<!-- html -->
<body>
  <!-- 这里使用的是路径,注意后续对a标签默认事件的处理 -->
  <a href="/1" class="link">one</a>
  <a href="/2" class="link">two</a>
  <a href="/3" class="link">three</a>
  <a href="/4" class="link">four</a>

  <div id="div404" style="display:none">该页面不存在</div>
  <div id="app"></div>
  <script src="./index.js"></script>
</body>
// js 
// 需要展示的各个Dom
const div1 = document.createElement("div");
div1.innerHTML = "one";
const div2 = document.createElement("div");
div2.innerHTML = "two";
const div3 = document.createElement("div");
div3.innerHTML = "three";
const div4 = document.createElement("div");
div4.innerHTML = "four";
const app = document.querySelector("#app");

// 路由表
const routeMap = {
  "/1": div1,
  "/2": div2,
  "/3": div3,
  "/4": div4
};

route(app);

function route(container) {
  // history的路由的key是路径名
  let number = window.location.pathname;
  // 默认路由
  if (number === "/") {
    number = "/1";
  }
  container.innerHTML = "";
  let div = routeMap[number];
  // 404路由
  if (!div) {
    div = document.querySelector("#div404");
  }
  div.style.display = "block";
  container.appendChild(div);
}

const allA = document.querySelectorAll("a.link");
for (let a of allA) {
  // 监听a标签的click事件,每次点击修改url,同时渲染对应的内容
  a.addEventListener("click", e => {
    // 取消a标签的跳转功能
    e.preventDefault();
    // 通过history的api来操作url
    window.history.pushState(null, null, a.href);
    route(app);
  });
}