如今,如果不使用 Next.js 或 Nuxt.js,现代大型项目是无法想象的。
但是,尽管如此,如果任务是快速创建这样的结构,那么这里描述的方法非常适合此目的。
今天,我们将创建一个位于服务器上的包含 5 个组件的小型登陆页面应用程序。
让我们开始吧!
应用程序结构
我们的应用程序将具有与现代 SSR 应用程序一样的结构(当然没有 BFF 等),但渲染将发生在客户端,并通过浏览器显示。
我们的架构中没有数据库的概念,因为数据将存储在 HTML 文件中。但如果我们在落地页上进行注册,我们就会有一个.json能够兼容现代数据库的文件,但这个例子应该在 10 分钟内完成,因此扩展功能毫无意义。
从哪里开始创建应用程序?
首先,让我们创建两个文件global.css和global.js。它们将包含那些不依赖于服务器的样式和脚本。
全局.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Roboto, sans-serif;
}
body {
line-height: 1.6;
color: #333;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.section {
padding: 80px 0;
text-align: center;
}
.section h2 {
font-size: 36px;
margin-bottom: 30px;
}
global.js
console.log("Global scripts loaded");
因此,可以不连接 global.js,但总的来说,如果我们在 js 上有共同点,那么作为示例会很棒。配置常量、实用函数等。
现在,我们将创建index.html,其中我们将连接登陆页面所需的所有模块。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Landing Page</title>
<link rel="stylesheet" href="global.css" />
</head>
<body>
<script src="https://unpkg.com/json5/dist/index.min.js"></script>
<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
<script src="global.js"></script>
</body>
</html>
该网站本身目前看起来是空的,所以让我们创建我们的组件!
⚙️ 服务器配置
对于服务器,我们当然会选择 Node.js 平台。您可以选择任何平台,它对网站来说并非必需。框架将使用 Express.js。
应用程序.js
const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const cors = require("cors");
const PORT = 8000;
const app = express();
const getRoutes = require("./routes/get");
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors({ origin: true, credentials: true }));
app.use(express.static(path.join(__dirname, "src")));
app.get("/", (_, res) => {
res.sendFile(path.join(__dirname, "src/index.html"));
});
app.use("/api", getRoutes);
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
路线/get.js
const express = require("express");
const expressRouter = express.Router();
const path = require("path");
const components = {
title: "CTA",
header: "Header",
features: "Features",
promo: "Promo",
cta: "CTA",
footer: "Footer",
};
Object.entries(components).forEach(([name, folder]) => {
expressRouter.get(`/get-${name}-component`, (_, res) => {
res.type("text/html");
res.sendFile(path.join(__dirname, `../components/${folder}/index.html`));
});
});
module.exports = expressRouter;
描述了几个 js 文件之后,我们现在可以在components文件夹中创建我们的应用程序部分。
路线可以任意命名,但是为了方便起见,我将其命名为/api/get-${name}-component
⌨️ 编写第一个组件
让我们从横幅广告开始,因为这是落地页的第一个内容块。我们将直接从 URL 处的服务器路由执行此操作http://localhost:8000/api/get-features-component。
组件/功能/index.html
<div id="features-component">
<section id="features" class="section features">
<div class="container">
<h2>Our Amazing Features</h2>
<div class="features-grid">
<div class="feature-card">
<h3>Fast</h3>
<p>Lightning fast performance that saves you time.</p>
</div>
<div class="feature-card">
<h3>Simple</h3>
<p>Easy to use interface with no learning curve.</p>
</div>
<div class="feature-card">
<h3>Reliable</h3>
<p>99.9% uptime guaranteed for your business.</p>
</div>
</div>
</div>
</section>
<style>
.features {
background: #f9f9f9;
padding: 80px 0;
text-align: center;
}
.features h2 {
font-size: 36px;
margin-bottom: 30px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
margin-top: 50px;
}
.feature-card {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateY(20px);
transition: all 0.6s ease;
}
.feature-card h3 {
margin-bottom: 15px;
font-size: 22px;
}
@media (max-width: 768px) {
.features-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
const animateFeatures = function () {
const elements = document.querySelectorAll(
"#features-component .feature-card"
);
elements.forEach((element) => {
const elementPosition = element.getBoundingClientRect().top;
const screenPosition = window.innerHeight / 1.3;
if (elementPosition < screenPosition) {
element.style.opacity = "1";
element.style.transform = "translateY(0)";
}
});
};
window.addEventListener("load", animateFeatures);
window.addEventListener("scroll", animateFeatures);
</script>
</div>
现在,让我们看看它会是什么样子:
是的,服务器上的组件看起来不太好,因为我们编写的样式只针对它。但是,当我们将所有这些组件部署到生产站点时,所有字体和其他内容都会连接起来,站点看起来会很好。
✅ 让我们把剩下的写完
一切正常,现在我们可以完成所有组件的编写,并使用 HMPL 将它们连接到我们的index.html。我们还将完成以下组件的编写:
标头:
http://localhost:8000/api/get-header-component促销:
http://localhost:8000/api/get-promo-component行动号召:
http://localhost:8000/api/get-cta-component页脚:
http://localhost:8000/api/get-footer-component
您可以在本站的代码库中找到完整的列表,我不会直接复制粘贴代码,因为这篇文章篇幅太长了。代码库的链接如下。
将所有内容连接到我们的网站
让我们将服务器请求的组件添加到我们的 html 中并附加生成的 html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Landing Page</title>
<link rel="stylesheet" href="global.css" />
</head>
<body>
<script src="https://unpkg.com/json5/dist/index.min.js"></script>
<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
<script src="global.js"></script>
<script>
const body = document.querySelector("body");
const template = `
<main>
<!-- Header Component -->
{{#request src="http://localhost:8000/api/get-header-component"}}{{/request}}
<!-- Features Component -->
{{#request src="http://localhost:8000/api/get-features-component"}}{{/request}}
<!-- Promo Component -->
{{#request src="http://localhost:8000/api/get-promo-component"}}{{/request}}
<!-- CTA Component -->
{{#request src="http://localhost:8000/api/get-cta-component"}}{{/request}}
<!-- Footer Component -->
{{#request src="http://localhost:8000/api/get-footer-component"}}{{/request}}
</main>
`;
const { response } = hmpl.compile(template)();
body.append(response);
</script>
</body>
</html>
值得注意的是,您可以为我们的组件添加加载器,或者通过interval属性为促销添加间隔请求。
️ 结果
现在,让我们看看我们在 10 分钟(多一点)的工作中完成了什么:
对我来说,它看起来非常漂亮,因为没有特别关注风格等。
️ 结论
在本文中,我们仅用 10 分钟就创建了一个小巧但非常酷炫且功能强大的客户端应用程序,它使用了 SSR 方法。使用 Next.js 也可以实现同样的效果,但前提是我们必须连接框架并完全依赖其结构。在这里,我们只需连接一个模块即可获得相同的效果(无需索引 robots)。

