Vanilla JavaScript 哈希 URL 路由器

我看到一篇关于如何在纯 Vanilla JavaScript 中创建路由器的帖子。由于它不是在讨论散列路由,因此我决定创建这篇文章来与您分享我的知识。

我为什么要这么做?

多亏了 History API,在 JavaScript 中构建路由器现在变得很简单。它在很大程度上得到支持,让您可以按照自己的方式构建路由器,而无需依赖第三方库。在 Vue.js 中,您甚至可以构建自己的自制路由器并将其插入,这要归功于 Vue.js 插件。事实上,Vue.js 的官方路由器库 Vue Router 依赖于 History API。但不仅如此。因为它可以选择让您构建所谓的散列路由器。

什么是散列路由器?

它是一个不依赖 History API 而是依赖于您网站的哈希 URL 的路由器。假设您的网页上有一个标题。

<h2>About me</h2>
<p>Lorem ipsum dolor sit amet...</p>

并且您希望您的用户在页面顶部时直接跳转到此部分。你会想id为你的标题使用一个属性。

<h2 id="about">About me</h2>

并创建一个链接,例如在您的标题中,将您的用户重定向到此部分。

<header>
  <a href="#about">About me</a>
</header>

现在,如果您单击链接,URL 应该来自

http://yoursite.com

http://yoursite.com#about

没什么花哨的,对吧?

为什么我要使用散列路由器?

基于历史 API 的路由器的问题在于它依赖于页面的来源来工作。如果您尝试打开实现 History API 的 HTML 页面,您应该会看到如下内容:

Failed to execute 'pushState' on 'History': A history state object with URL 'file:///path/to/index.html' cannot be created in a document with origin 'null'

这是因为默认情况下,作为文件打开的 HTML 文档的来源设置为null. 这是个问题。

相反,基于 hased 的路由器不依赖于它,而是依赖于窗口对象触发的事件。当我们更改 url 的哈希时,将触发此事件。在我们之前的示例中,单击链接会触发此事件。不需要网络服务器。

如何实现散列路由器?

它就像只使用一个事件一样简单。事件onHashChange

<!DOCTYPE html>
<html>
  <body>
    <a href="#home">Home</a>
    <a href="#about">About</a>
    <script src="script.js"></script>
  </body>
</html>
function onRouteChanged() {
  console.log("Hash changed!");
}

window.addEventListener("hashchange", onRouteChanged);
Hash changed!
Hash changed!

在线尝试

实现路由

我们现在需要获取用户发出的路由。我们可以使用该window.location.hash属性来获取当前“路线”的值。

function onRouteChanged() {
   console.log(window.location.hash);
  }
#home
#about

在线尝试

我们现在拥有我们需要的一切。我们可以开始为我们的路由器实现视图渲染器。

      <a href="#about">About</a>
      <a href="#contact">Contact</a>
      <main id="router-view"></main>
      <script src="script.js"></script>

我还添加了另一个链接。这将帮助我向您展示我们如何实现404 - page not found处理程序。你会惊讶于它是多么容易。

接下来,我们需要向我们的处理程序添加更多逻辑,onRouteChange以便它可以像路由器一样呈现我们的路由。

function onRouteChanged() {
   const hash = window.location.hash;
   const routerView = document.getElementById("router-view");
 
   if (!(routerView instanceof HTMLElement)) {
     throw new ReferenceError("No router view element available for rendering");
   }
 
   switch (hash) {
     case "#home":
       routerView.innerHTML = "<h1>Home page</h1>";
       break;
 
     case "#about":
       routerView.innerHTML = "<h1>About page</h1>";
       break;
 
     default:
       routerView.innerHTML = "<h1>404 - Page Not Found</h1>";
       break;
   }
}

在线尝试

我将哈希 URL 存储在一个变量中,以便我可以使用该switch语句根据用户发出的路由呈现不同的 HTML 内容。我还存储了路由器视图元素以检查该元素是否确实在文档中(我们永远不知道会发生什么,在这种情况下有一些雄辩的错误消息会很好)。我还需要它来更新语句innerHTML中路由器的内容。switch

switch 的默认语句将通过我们的联系链接触发,因为我们没有在 switch 中为它指定任何处理程序。

而已!您有一个非常基本的路由器,可以在任何地方工作,无论是托管在 Web 服务器上,还是作为单个 HTML 页面共享。例如,当您需要向客户展示网站的快速原型时,我可以看到一些用例。他所要做的就是在他的浏览器中打开页面,然后tada!

限制

当然,这种路由有一个明显的限制,因为我们使用的是 URL 的哈希值,并破解了它的初衷,将其用作路由器。但是如果我们需要在我们的页面中使用常规的hrefs,它只会破坏路由,因为它会触发我们的哈希更改处理程序。

解决方案

我为这个问题找到的一个解决方案可能不是最好的,但如果你绝对需要使用基于散列的路由,那么它是值得的,它是使用data-*属性和一点 JavaScript。

<button data-go-to-id="a-little-introduction">to the intro</button>
<!-- later in the document -->
<h2 id="a-little-introduction>A little introduction</h2>
"use strict";

document.querySelectorAll("[data-go-to-id]").forEach(function(link) {
  link.addEventListener("click", function() {
    const element = document.getElementById(link.dataset.goToId);

    if (!(element instanceof HTMLElement)) {
      throw new ReferenceError(`Unable to found element with id "${goToId}"`);
    }

    window.scroll({
      top: element.getBoundingClientRect().top,
      left: 0,
      behavior: "smooth"
    });
  });
});

某些设备上的平滑滚动不起作用(我特别想到了一些 Apple 设备),但我敢肯定,这将是您可以找到解决此问题的许多解决方案之一。我的解决方案的缺点是它不能用于共享链接,如Hey, look what I found here: http://yoursite.com#home#title-of-article. 我将此作为练习,供读者实施更好的解决方案。

结论

基于哈希的路由器是另一种无需重新加载页面即可路由用户的方式。这在创建 GitHub 页面时也很方便,因为我们不必重新考虑基于历史记录的路由器并在所有路由前加上/github-repo/about.

如果您不需要使用大量的 href 重定向并且不想/不能使用 History API,那么在您的页面中使用路由器可能是一个很好的解决方案。

我向您展示的是基于 hased 的路由器的一个非常基本的实现。如果你想更进一步,你可以:

  • 在一个对象中实现这个路由器,就像new HashedRouter使 API 更易于使用一样。addRoute特别是使用和之类的方法start
  • 找到比我用来在页面中实现链接更好的解决方案。
'use strict';

function onRouteChanged() {
    const hash = window.location.hash,
            routerView = document.querySelector('#router-view');

    if (!(routerView instanceof(HTMLElement))) {
        throw new ReferenceError('No router view element available for rendering');
    }

    switch (hash) {
        case '#home':
            routerView.innerHTML = '<object type="text/html" data="views/home.html"></object>';
            break;
        case '#about':
            routerView.innerHTML = '<object type="text/html" data="views/about.html"></object>';
            break;
        default:
            routerView.innerHTML = '<object type="text/html" data="views/404.html"></object>';
            break;
    }
    //console.log(hash);
}

window.addEventListener('hashchange', onRouteChanged);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值