使用 Vanilla JavaScript 构建自定义 SPA 路由器

本文介绍了如何在不使用框架的情况下,利用JavaScript构建一个简单的单页应用(SPA)路由器。通过理解window.history和window.location对象,结合pushState和popstate事件,实现了页面导航和URL更新。文章详细阐述了加载HTML页面、处理点击事件和页面呈现的过程,最后处理了浏览器前进后退按钮的状态变化问题。

介绍

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

免责声明

我同意我们不应该重新发明轮子的哲学。随着 ReactJS、Vue 等框架的出现,它们中的每一个都有自己的自定义路由库,我建议您使用它们而不是构建一些基础的东西。但是,本文的目的是解释使用 VanillaJS 编写自定义路由器是可能的,它还提供了一个了解幕后发生的事情的机会。

窗口 - 历史和位置对象

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

历史对象

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

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

位置对象

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

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

历史 - PushState()

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

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

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

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

窗口 - Popstate 事件

当用户浏览会话历史时活动历史发生变化时,会触发popstate 事件。

换句话说,只要在浏览器上按下后退或前进按钮,历史就会改变,此时会触发 'popstate' 事件。

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

实现路由器

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

风景

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

  • home
  • about
  • contact

此外,home、about和contact视图有 3 个单独的 HTML。

index.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>

home.html

<div>
  <h1>******Welcome to the Home Page*****</h1>
</div>

about.html

<div>
  <h1>******Welcome to the About Page*****</h1>
</div>

contact.html

<div>
  <h1>******Welcome to the Contact Page*****</h1>
</div>

加载 HTML 页面(异步)

我使用带有“fetch API”的 async/await 来异步加载页面,并使用“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');
};

让我们浏览一页的流程:

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

同样,API 调用也会针对“about”和“contact”页面进行,并将值填充到变量 about & contact。

主要功能和根元素

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

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

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

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

//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();

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

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

<li class="navbar-item">
    <a href="#" onclick="onNavClick('/about'); return false;">About</a>
</li>
/**
 *
 * @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];
};

onNavClick 方法接受“路径名”,即“路线”链接,并使用 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];
};

如果你想要完整的代码,你可以在 Github找到它。

结论

总而言之,我们已经介绍了如何使用 VanillaJS 构建基本的自定义路由器。路由器主要使用窗口的历史和位置对象以及方法 pushState 和 onpopstate 事件。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值