13 KiB
构建银行应用程序第1部分:Web应用中的HTML模板和路由
课前测验
简介
自从浏览器中出现JavaScript以来,网站变得比以往更加互动和复杂。如今,Web技术常被用来创建直接在浏览器中运行的功能齐全的应用程序,我们称之为Web应用程序。由于Web应用程序高度互动,用户不希望每次执行操作时都需要等待整个页面重新加载。因此,JavaScript被用来直接通过DOM更新HTML,以提供更流畅的用户体验。
在本课程中,我们将为创建银行Web应用程序奠定基础,使用HTML模板创建多个屏幕,这些屏幕可以显示和更新,而无需重新加载整个HTML页面。
前置条件
你需要一个本地Web服务器来测试我们将在本课程中构建的Web应用程序。如果你没有,可以安装Node.js,然后在项目文件夹中使用命令npx lite-server
。它会创建一个本地Web服务器并在浏览器中打开你的应用程序。
准备工作
在你的电脑上创建一个名为bank
的文件夹,并在其中创建一个名为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>Bank App</title>
</head>
<body>
<!-- This is where you'll work -->
</body>
</html>
HTML模板
如果你想为一个网页创建多个屏幕,一种解决方案是为每个屏幕创建一个HTML文件。然而,这种解决方案有一些不便之处:
- 切换屏幕时需要重新加载整个HTML,这可能会很慢。
- 在不同屏幕之间共享数据会变得困难。
另一种方法是只使用一个HTML文件,并使用<template>
元素定义多个HTML模板。模板是一个可重复使用的HTML块,浏览器不会显示它,需要在运行时通过JavaScript实例化。
任务
我们将创建一个银行应用程序,包含两个屏幕:登录页面和仪表盘页面。首先,在HTML主体中添加一个占位元素,我们将用它来实例化应用程序的不同屏幕:
<div id="app">Loading...</div>
我们给它一个id
,以便稍后通过JavaScript更容易找到它。
提示:由于这个元素的内容会被替换,我们可以放置一个加载消息或指示器,在应用程序加载时显示。
接下来,在HTML中添加登录页面的模板。现在我们只在其中放置一个标题和一个包含导航链接的部分。
<template id="login">
<h1>Bank App</h1>
<section>
<a href="/dashboard">Login</a>
</section>
</template>
然后我们添加仪表盘页面的HTML模板。这个页面将包含不同的部分:
- 一个包含标题和注销链接的页眉
- 银行账户的当前余额
- 一个显示交易记录的表格
<template id="dashboard">
<header>
<h1>Bank App</h1>
<a href="/login">Logout</a>
</header>
<section>
Balance: 100$
</section>
<section>
<h2>Transactions</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Object</th>
<th>Amount</th>
</tr>
</thead>
<tbody></tbody>
</table>
</section>
</template>
提示:创建HTML模板时,如果你想查看它的外观,可以通过将
<template>
和</template>
行用<!-- -->
包裹来注释掉它们。
✅ 你认为我们为什么在模板上使用id
属性?我们是否可以使用其他方法,比如类?
使用JavaScript显示模板
如果你在浏览器中尝试当前的HTML文件,你会发现它停留在显示Loading...
。这是因为我们需要添加一些JavaScript代码来实例化并显示HTML模板。
实例化模板通常分为三个步骤:
- 在DOM中检索模板元素,例如使用
document.getElementById
。 - 使用
cloneNode
克隆模板元素。 - 将其附加到可见元素的DOM中,例如使用
appendChild
。
✅ 为什么我们需要在附加到DOM之前克隆模板?如果跳过这一步会发生什么?
任务
在项目文件夹中创建一个名为app.js
的新文件,并在HTML的<head>
部分导入该文件:
<script src="app.js" defer></script>
现在在app.js
中,我们将创建一个新函数updateRoute
:
function updateRoute(templateId) {
const template = document.getElementById(templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
这里我们执行了上述的三个步骤。我们实例化templateId
对应的模板,并将其克隆的内容放入应用程序的占位符中。注意,我们需要使用cloneNode(true)
来复制模板的整个子树。
现在调用这个函数并传入一个模板,查看结果。
updateRoute('login');
✅ 代码app.innerHTML = '';
的作用是什么?如果没有它会发生什么?
创建路由
在谈论Web应用程序时,我们称路由为将URL映射到应该显示的特定屏幕的意图。在一个包含多个HTML文件的网站中,这会自动完成,因为文件路径会反映在URL中。例如,在项目文件夹中有以下文件:
mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html
如果你创建一个以mywebsite
为根的Web服务器,URL映射将是:
https://site.com --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
然而,对于我们的Web应用程序,我们使用的是一个包含所有屏幕的单一HTML文件,因此这种默认行为对我们没有帮助。我们必须手动创建这个映射,并使用JavaScript更新显示的模板。
任务
我们将使用一个简单的对象来实现一个映射,将URL路径与我们的模板关联。在app.js
文件顶部添加这个对象。
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard' },
};
现在稍微修改一下updateRoute
函数。我们不再直接传递templateId
作为参数,而是先查看当前URL,然后使用我们的映射获取对应的模板ID值。我们可以使用window.location.pathname
来仅获取URL中的路径部分。
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
这里我们将声明的路由映射到对应的模板。你可以通过在浏览器中手动更改URL来测试它是否正常工作。
✅ 如果在URL中输入一个未知路径会发生什么?我们如何解决这个问题?
添加导航
应用程序的下一步是添加在页面之间导航的功能,而无需手动更改URL。这包括两件事:
- 更新当前URL
- 根据新URL更新显示的模板
我们已经通过updateRoute
函数解决了第二部分,因此我们需要弄清楚如何更新当前URL。
我们需要使用JavaScript,特别是history.pushState
,它允许更新URL并在浏览历史中创建新条目,而无需重新加载HTML。
注意:虽然HTML锚元素
<a href>
本身可以用于创建指向不同URL的超链接,但默认情况下它会使浏览器重新加载HTML。在使用自定义JavaScript处理路由时,需要使用preventDefault()
函数阻止点击事件的默认行为。
任务
让我们创建一个新函数,用于在应用程序中导航:
function navigate(path) {
window.history.pushState({}, path, path);
updateRoute();
}
此方法首先根据给定路径更新当前URL,然后更新模板。属性window.location.origin
返回URL根路径,允许我们从给定路径重新构建完整URL。
现在我们有了这个函数,可以解决路径与定义的路由不匹配时的问题。我们将通过添加一个回退到现有路由的功能来修改updateRoute
函数。
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
...
如果找不到路由,我们现在会重定向到login
页面。
接下来创建一个函数,用于获取点击链接时的URL,并阻止浏览器的默认链接行为:
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
通过在HTML中的登录和注销链接上添加绑定,完成导航系统。
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
...
<a href="/login" onclick="onLinkClick(event)">Logout</a>
上面的event
对象捕获click
事件并将其传递给我们的onLinkClick
函数。
使用onclick
属性将click
事件绑定到JavaScript代码,这里是调用navigate()
函数。
尝试点击这些链接,你现在应该能够在应用程序的不同屏幕之间导航。
✅ history.pushState
方法是HTML5标准的一部分,并在所有现代浏览器中实现。如果你正在为旧版浏览器构建Web应用程序,可以使用一个技巧代替此API:在路径前使用哈希(#
),可以实现与常规锚导航兼容的路由,并且不会重新加载页面,因为它的目的是在页面内创建内部链接。
处理浏览器的后退和前进按钮
使用history.pushState
会在浏览器的导航历史中创建新条目。你可以通过按住浏览器的后退按钮来检查,它应该显示如下内容:
如果你尝试点击几次后退按钮,你会发现当前URL发生了变化,历史记录也更新了,但显示的模板保持不变。
这是因为应用程序不知道每次历史记录更改时需要调用updateRoute()
。如果你查看history.pushState
文档,你会发现当状态更改时——即我们移动到不同的URL——会触发popstate
事件。我们将利用这一点来解决问题。
任务
为了确保浏览器历史记录更改时显示的模板得到更新,我们将附加一个新函数来调用updateRoute()
。我们将在app.js
文件底部完成此操作:
window.onpopstate = () => updateRoute();
updateRoute();
注意:我们在这里使用了箭头函数来声明
popstate
事件处理程序以简化代码,但普通函数也可以正常工作。
以下是关于箭头函数的复习视频:
🎥 点击上方图片观看关于箭头函数的视频。
现在尝试使用浏览器的后退和前进按钮,检查显示的路由是否正确更新。
🚀 挑战
为第三个页面添加一个新模板和路由,该页面显示此应用程序的致谢信息。
课后测验
复习与自学
路由是Web开发中一个令人惊讶的复杂部分,尤其是在Web从页面刷新行为转向单页应用程序页面刷新时。阅读一些关于Azure静态Web应用服务如何处理路由的内容。你能解释为什么文档中描述的一些决策是必要的吗?
作业
免责声明:
本文档使用AI翻译服务 Co-op Translator 进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。对于因使用本翻译而引起的任何误解或误读,我们概不负责。