在本文中,我们将构建一个客户端路由系统。客户端路由是一种路由类型,用户在其中导航应用程序,即使页面的 URL 发生更改,也不会重新加载整个页面,而是显示新内容。
为了构建它,我们需要一个简单的服务器来提供我们的index.html
文件。准备好?让我们开始。
让我们为我们的 HTML 创建一个简单的样板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Javascript Routing</h1>
<div id="app">
</div>
<script src="main.js"></script>
</body>
</html>
在这里,我们将链接main.js
文件,以便我们可以在任何时间点操作 DOM。
实现路由系统
让我们转到main.js
文件并编写我们所有的路由器逻辑。我们所有的代码都将被包装在window.onload
其中,以便它们仅在网页完全加载其所有内容后才执行脚本。
接下来,我们将创建一个路由器实例,它是一个带有两个参数的函数。第一个参数将是路线的名称,第二个参数将是一个包含我们定义的所有路线的数组。这条路线将有两个属性:路线的名称和路线的路径。
window.onload = () => {
// get root div for rendering
let root = document.getElementById('app');
//router instance
let Router = function (name, routes) {
return {
name,
routes
}
};
//create the route instance
let routerInstance = new Router('routerInstance', [{
path: "/",
name: "Root"
},
{
path: '/about',
name: "About"
},
{
path: '/contact',
name: "Contact"
}
])
}
我们可以获取我们页面当前的路由路径,并根据该路由显示一个模板。location.pathname
返回页面的当前路由,我们可以将这段代码用于我们的 DOM:
let currentPath = window.location.pathname;
if (currentPath === '/') {
root.innerHTML = 'You are on Home page'
} else {
// check if route exist in the router instance
let route = routerInstance.routes.filter(r => r.path === currentPath)[0];
if (route) {
root.innerHTML = `You are on the ${route.name} path`
} else {
root.innerHTML = `This route is not defined`
}
}
我们将使用该currentPath
变量来检查路由实例中是否定义了路由。如果路由存在,我们将呈现一个简单的 HTML 模板。如果没有,我们将This route is not defined
显示在页面上。
随意显示您选择的任何形式的错误。例如,如果路由不存在,您可以将其重定向回主页。
添加路由器链接
对于页面导航,我们可以添加路由器链接。就像使用Angular一样,您可以传递一个routerLink
具有您要导航到的路径的值的值。为了实现这一点,让我们在index.html
文件中添加一些链接:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<nav>
<button router-link="/">Home</button>
<button router-link="/about">About</button>
<button router-link="/contact">Contact</button>
<button router-link="/unknown">Error</button>
</nav>
<h1>Javascript Routing</h1>
<div id="app">
</div>
<script src="main.js"></script>
</body>
</html>
注意我们传入的router-link
属性——这是我们将用于路由的属性。
我们将创建一个存储所有的router-link
变量并将其存储在一个数组中:
let definedRoutes = Array.from(document.querySelectorAll('[router-link]'));
在将我们的路由链接存储在一个数组中之后,我们可以遍历它们并添加一个调用navigate()
函数的点击事件侦听器:
//iterate over all defined routes
definedRoutes.forEach(route => {
route.addEventListener('click', navigate, false)
})
定义导航功能
导航功能将使用Javascript History API进行导航。history.pushState()
方法将状态添加到浏览器的会话历史堆栈。
单击按钮时,我们将收到该按钮的路由器链接属性,然后使用history.pushState()
导航到该路径,然后更改呈现的 HTML 模板:
// method to navigate
let navigate = e => {
let route = e.target.attributes[0].value;
// redirect to the router instance
let routeInfo = routerInstance.routes.filter(r => r.path === route)[0]
if (!routeInfo) {
window.history.pushState({}, '', 'error')
root.innerHTML = `This route is not Defined`
} else {
window.history.pushState({}, '', routeInfo.path)
root.innerHTML = `You are on the ${routeInfo.name} path`
}
}
如果一个 nav 链接有一个未在 中定义的路由器链接routeInstance
,它会将推送状态设置为This route is not Defined
并在模板上error
渲染。
接下来,您应该考虑将路由存储在单独的文件中,这样可以使代码更整洁,并且在出现任何错误时更易于调试。现在,创建routes.js
文件并将路由构造函数和路由器实例提取到这个新文件中:
//router instance
let Router = function (name, routes) {
return {
name,
routes
}
};
let routerInstance = new Router('routerInstance', [{
path: "/",
name: "Root"
},
{
path: '/about',
name: "About"
},
{
path: '/contact',
name: "Contact"
}
])
export default routerInstance
导出此文件使其可供其他 JavaScript 文件访问。我们可以将它导入到我们的 main.js 文件中:
import routerInstance from './routes.js'
这将引发错误。要修复它,请将 index.html 文件中的脚本标记修改为:
<script type="module" src="main.js"></script>
添加模块的类型指定可以在模块外部访问哪些变量和函数。
结论
了解如何在 Vanilla JavaScript 中实现路由系统可以让开发人员更轻松地使用框架路由库,例如Vue.js 路由器。我们这里的代码可以在单页应用程序中重用,当您在没有框架的情况下工作时这是完美的。要获取源代码,请查看GitHub。