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

被折叠的 条评论
为什么被折叠?



