使用 VanillaJS 构建自定义 SPA 路由器 简介窗口 - 历史记录和位置对象 实现路由器 结论 RouteNow

2025-06-11

使用 VanillaJS 构建自定义 SPA 路由器

介绍

窗口 - 历史记录和位置对象

实现路由器

结论

路线现在

介绍

在本文中,我将解释如何使用 Vanilla JavaScript 构建自定义 SPA 路由器。我必须构建一个不使用任何框架的 UI 项目,并且必须弄清楚如何处理路由,然后发现可以使用 Vanilla JavaScript 构建自己的路由器。

免责声明

我完全赞同这样的理念:我们不应该把时间浪费在那些已经很好解决的问题上。随着框架的出现,市面上有很多现成的路由器可供使用。本文旨在解释如何使用 VanillaJS 编写自定义路由器,并帮助理解其背后的原理。

窗口 - 历史记录和位置对象

为了构建自定义路由器,我们首先需要了解“窗口”对象的“历史记录”和“位置”对象以及处理页面导航所需的一些方法。

历史对象

window.history对象提供有关浏览器会话历史记录的详细信息。它包含一些方法和属性,可帮助您在用户历史记录中来回导航。

您可以打开浏览器控制台并输入 history,您将看到 history 对象的所有方法和属性,如下所示。

替代文本

位置对象

window.location包含与当前位置相关的所有信息,例如原点、路径名等

您可以打开浏览器控制台并输入位置,您将看到与位置对象相关的所有各种属性和方法,如下所示。

替代文本

历史记录 - pushState()

方法pushState用于将状态添加到浏览器的会话历史堆栈。

语法:history.pushState(state, title, [, url]);

  • state - 与新历史记录条目关联的 JavaScript 对象。状态对象可以是任何可序列化的对象。
  • title - 现代浏览器实际上尚未使用标题。传递空字符串或您希望引用状态的标题是安全的。
  • url - 此参数指定新的历史记录条目的 URL。

我们将使用 pushState 方法在页面导航期间更新浏览器的 URL。

窗口 - popstate 事件

当用户浏览会话历史记录时,如果活动历史记录发生变化,则会触发popstate 事件

换句话说,每当在浏览器上按下后退或前进按钮时,历史记录就会发生变化,此时 popstate 事件就会被触发。

每当历史记录发生变化时,我们将使用 popstate 事件来处理逻辑。

实现路由器

现在我们已经掌握了基础知识,我们将逐步介绍使用 VanillaJS 实现路由器的方法。

观点

index.html 是一个非常简单的页面,其中包含页面链接的无序列表 -

  • 关于
  • 接触

此外,还有 3 个单独的 HTML,分别用于主页、关于和联系视图。

索引.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vanilla JS Router</title>
  </head>
  <body>
    <ul class="navbar-list">
      <li class="navbar-item">
        <a href="#" onclick="onNavClick('/about'); return false;">About</a>
      </li>
      <li class="navbar-item">
        <a href="#" onclick="onNavClick('/'); return false;">Home</a>
      </li>
      <li class="navbar-item">
        <a href="#" onclick="onNavClick('/contact'); return false;">Contact</a>
      </li>
    </ul>
    <div id="root"></div>
    <script src="./js/app.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

主页.html

<div>
  <h1>******Welcome to the Home Page*****</h1>
</div>
Enter fullscreen mode Exit fullscreen mode

关于.html

<div>
  <h1>******Welcome to the About Page*****</h1>
</div>
Enter fullscreen mode Exit fullscreen mode

联系方式.html

<div>
  <h1>******Welcome to the Contact Page*****</h1>
</div>
Enter fullscreen mode Exit fullscreen mode

加载 HTML 页面(异步)

我使用 async/await 和“fetch API”来异步加载页面,并使用“promise”为 home、about 和 contact 变量赋值。

//Declare the variables for home, about & contact html pages
let home = '';
let about = '';
let contact = '';

/**
 *
 * @param {String} page - Represents the page information that needs to be retrieved
 * @returns {String} resHtml - The Page's HTML is returned from the async invocation
 */

const loadPage = async (page) => {
  const response = await fetch(page);
  const resHtml = await response.text();
  return resHtml;
};

/**
 * The Async function loads all HTML to the variables 'home', 'about' & 'contact'
 */
const loadAllPages = async () => {
  home = await loadPage('home.html');
  about = await loadPage('about.html');
  contact = await loadPage('contact.html');
};
Enter fullscreen mode Exit fullscreen mode

让我们来看一下一个页面的流程:

  • 当调用“loadAllPages”函数时,第一个函数loadPage('home.html')首先被触发。
  • 在“loadPage”函数内部,将触发 fetch('home.html') 来异步加载 home.html。
  • 由于文本是在 API 调用中返回的,因此“await”关键字可确保“response”变量被填充,并且“resHtml”被分配“response.text()”。
  • “resHtml”的值返回给“loadAllPages”函数并分配给“home”变量。

同样,也会对“关于”和“联系”页面进行 API 调用,并将值填充到变量 about 和 contact 中。

主要功能和根元素

从“index.html”文档中获取“rootDiv”。

页面加载时,main 函数会被调用。在 main 函数内部,我们首先确保所有 HTML 页面都加载到变量“home”、“about”和“contact”中。

为了确保在页面加载时将“home”页面加载到根元素,rootDiv.innerHTML 被设置为“home”变量。

此外,使用相应的页面映射来设置“路线”,以便在调用路线时加载适当的页面。

//Get the Element with the Id 'root'
const rootDiv = document.getElementById('root');

/**
 * The Main Function is an async function that first loads All Page HTML to the variables
 * Once the variables are loaded with the contents, then they are assigned to the 'routes' variable
 */
const main = async () => {
  await loadAllPages();
  rootDiv.innerHTML = home;
  routes = {
    '/': home,
    '/contact': contact,
    '/about': about,
  };
};

// Invoke the Main function
main();
Enter fullscreen mode Exit fullscreen mode

路由 - 在主页上单击链接时

从上面的 index.html 中,我们调用“onNavClick”方法并在单击“a”链接时传入“route”,如下面的代码片段所示。

<li class="navbar-item">
    <a href="#" onclick="onNavClick('/about'); return false;">About</a>
</li>
Enter fullscreen mode Exit fullscreen mode
/**
 *
 * @param {String} pathname - Pass the 'pathname' passed from onClick function of the link (index.html)
 * The function is invoked when any link is clicked in the html.
 * The onClick event on the html invokes the onNavClick & passes the pathname as param
 */
const onNavClick = (pathname) => {
  window.history.pushState({}, pathname, window.location.origin + pathname);
  rootDiv.innerHTML = routes[pathname];
};
Enter fullscreen mode Exit fullscreen mode

onNavClick 方法接受“pathname”,即“route”链接,并使用 window.history.'pushState' 方法来改变状态。

第二行“rootDiv.innerHTML = routes[pathname]”将根据主函数中路由的配置呈现适当的页面(见上文)。

此时,您有一个功能齐全的路由器,单击链接后它会导航到相应的页面,并且相应的链接也会在 URL 浏览器中更新。

您唯一会注意到的是,当您点击浏览器上的“后退”或“前进”按钮时,链接会在 URL 上正确更新,但页面上的内容不会刷新。

让我们在文章的最后一部分来解决这个问题。

处理状态改变时的页面渲染

如果您还记得上面“onpopstate event”方法的定义,那么每当浏览器中的活动历史记录发生变化时就会调用该方法。

我们使用该钩子来确保根据配置的路由使用适当的页面填充 rootDiv。

就是这样!现在你应该拥有一个功能齐全的自定义路由器,全部使用 Vanilla JavaScript 构建。

/**
 * The Function is invoked when the window.history changes
 */
window.onpopstate = () => {  
  rootDiv.innerHTML = routes[window.location.pathname];
};
Enter fullscreen mode Exit fullscreen mode

如果您想要完整的代码,您可以在这里的Github 上找到它。

结论

总而言之,我们介绍了如何使用 VanillaJS 构建一个基本的自定义路由器。该路由器主要使用窗口的 history 和 location 对象以及 pushState 和 onpopstate 事件方法。

希望你喜欢这篇文章。请告诉我你的反馈和评论。

您可能还对以下内容感兴趣:

鏂囩珷鏉ユ簮锛�https://dev.to/skaytech/build-a-custom-spa-router-using-vanillajs-4a9l
PREV
开始使用 AWS 无服务器:强大的 Lambda 类型!
NEXT
学习音乐理论的最佳新方法