🌷使用 HMPL 在 JavaScript 中创建画廊应用程序

大家好!在本文中,我将介绍创建 Gallery 应用程序的过程。您可以放心地使用此应用程序并根据需要对其进行编辑(您只能在那里更改图片,因为有许可证)。它的功能很小,但在我看来,它非常适合用作工作示例。

该应用程序是什么样的以及它的功能是什么?

该应用程序是一个小型图像列表,可以浏览其页面。界面如下所示:

**桌面**

Desktop

**移动的**

Mobile

在功能方面,您可以单击“下一步”按钮转到下一页,单击“上一页”按钮可以返回第一页。

Second page

另外,如果你点击任意一个图像,你就可以看到它的完整格式:

full format

这是该应用程序中提供的主要功能。

应用程序的微妙之处

此应用程序的主要功能之一是**图像(如标题文本)来自服务器**。也就是说,我们不会在客户端的站点存储库中存储 10 幅图像。它们全部来自服务器。这可以通过以下方法实现:我们将主要内容的 HTML 存储在服务器上,然后在客户端将其输出到一些单元格,这实际上占用的磁盘空间很少。

请记住,当您从远程存储库克隆文件时,如果代码很少,则克隆视频或图像可能会花费大量时间。这里也是如此。这是此类应用程序的主要优势之一。

此外,在客户端浏览器上,如果我们考虑应用程序的加载,当用户首次进入网站时,它可以加载几秒钟。他可以关闭此资源并转到另一个资源,因此从金钱方面来说,这在某些情况下可以为您节省预算。

这种方法是面向服务器的,但不是服务器端渲染,因为组件是在客户端渲染的,机器人不会看到结果。

无论如何,现在有这样一种创建网站的方法,而且它非常方便,有其优点。如今有相当多的库实现了类似的功能。其中之一就是 HMPL。

开发过程和代码本身

首先,您需要选择编写应用程序的平台。我们所说的平台是指后端的**Express.js**,一般是指**Node.js**,而在客户端上,我们将有一个简单的**Webpack**程序集。

客户端

对于从哪里开始,有不同的方法。从服务器或从客户端开始。在我们的例子中,最好从客户端开始,因为在服务器上我们知道图像列表和标题已经生成,但如何最好地将它们集成到 DOM 中 - 这正是我们需要首先弄清楚的。

让我们继续查看原始 HTML 文件:

**索引.html**



  
    
    
    Gallery App
  
  

看起来这里什么都没有,是的,你是对的。作为内容的组件将被加载到此文件中。我们将在 `.hmpl` 扩展文件中创建它们,这会稍微扩展 html 的功能。

为此,我们将创建一个“components”文件夹,用于存储这些文件。我们将通过 JavaScript 将每个文件连接到页面。它们的标记:

**画廊.hmpl**

值得注意的是这里标记了两个对象,第一个是在页面加载时触发,第二个是在点击导航按钮后触发。

**标题.hmpl**

{{ src: "http://localhost:8000/api/title" }}

在这里,对象将从服务器更改为 HTML。现在,它们应该连接起来。为此,将它们导入到 main.js 中:

import "./index.scss";
import GalleryTemplate from "./components/Gallery/Gallery.hmpl";
import TitleTemplate from "./components/Title/Title.hmpl";

const { response: Title } = TitleTemplate();

const { response: Gallery } = GalleryTemplate(({ request: { event } }) => {
  return {
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      page: event ? Number(event.target.getAttribute("data-page")) : 1,
    }),
  };
});

document.body.append(Title);
document.body.append(Gallery);

const gallery = document.querySelector("#gallery");
const galleryInitial = document.querySelector("#gallery-initial");
const modal = document.querySelector("#modal");
const modalImg = modal.querySelector("img");
const navigationButtons = document.querySelectorAll(".navigation-button");

const setActive = (e) => {
  if (e.target.tagName === "IMG") {
    modalImg.src = e.target.src;
    modal.classList.add("active");
  }
};

modal.addEventListener("click", () => {
  modal.classList.remove("active");
});

galleryInitial.addEventListener("click", (e) => {
  setActive(e);
});

gallery.addEventListener("click", (e) => {
  setActive(e);
});

for (let i = 0; i < navigationButtons.length; i++) {
  const btn = navigationButtons[i];
  btn.addEventListener("click", () => {
    if (!galleryInitial.classList.contains("hidden"))
      galleryInitial.classList.add("hidden");
    btn.setAttribute("disabled", "");
    navigationButtons[i === 0 ? 1 : 0].removeAttribute("disabled");
  });
}

此外,在 `main.js` 中,我们将描述应用程序的逻辑。在这里,我们向服务器发送请求并接收 HTML,我们尚未准备好,但将在开发过程中准备。由于服务器 HTML 位于 `div` 块中,我们可以轻松地将组件添加到 DOM 而无需等待响应。

这里需要在按钮的 `disabled` 属性之间添加另一个开关。理想情况下,值得从服务器获取页面数量并专注于此,但由于应用程序本身很小,并且所有常量都是预先知道的,因此最好不要用额外的代码来重载它。顺便说一句,图像更改本身将在向 API 发出请求时自动发生。

并且还需要在点击时显示图像 - 这是通过在包装标签上挂一个事件并确定如果是点击图像则必须相应地激活该块来完成的。

我们包含的样式如下:

**索引.scss**

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #f4f4f4;
}

h1 {
  margin: 20px 0;
  color: #333;
}

.gallery-initial.active {
  display: flex;
}

.gallery,
.gallery-initial {
  display: flex;
  gap: 20px;
  width: 90%;
  max-width: 1000px;

  @media (max-width:1023px) {
   display: grid;
   grid-template-columns: repeat(2, 1fr);
   max-width: unset;
   justify-content: center;
   align-items: center;
   width: 100%;
  }
}

.hidden {
  display: none;
}

.gallery img,
.gallery-initial img {
  width: 150px;
  height: 100px;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  cursor: pointer;
  transition: transform 0.2s;
}

.gallery img:hover,
.gallery-initial img:hover {
  transform: scale(1.05);
}

.modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  justify-content: center;
  align-items: center;
}

.modal img {
  max-width: 90%;
  max-height: 90%;
  border-radius: 10px;
}

.modal.active {
  display: flex;
}

.pagination {
  margin: 20px 0;
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
}

.pagination button {
  padding: 10px 20px;
  border: none;
  background-color: #333;
  color: #fff;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.pagination button:hover {
  background-color: #555;
}

.pagination button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

风格简约,只是为了让画廊看起来或多或少有点美观。

另外,我通常使用很久以前制作的现成的 webpack 程序集(我必须为框架制作一个网站),但现在没有必要纠结于 `webpack.config.js` 中每个点负责什么。该文件可以在此处查看。

现在,是时候转到后端了。

后端

创建后端时,我们现在可以冷静地查看客户端,并在此基础上创建所需的路线。假设我创建了一个画廊 - 很好,然后我需要下载图片并设置那里描述的路线。

我们看到需要创建的路由是 `/api/images`:

src: "http://localhost:8000/api/images",

现在,您只需为其准备响应中发出的 HTML 标记。此外,路由的方法将是“POST”,因为在“RequestInit”的“主体”中,您需要传递具有所需值的“page”。让我们设置一个类似的路由:

**routes/post.js**

const express = require("express");
const expressRouter = express.Router();

const imagePaths = [
  "http://localhost:8000/images/img1.jpg",
  "http://localhost:8000/images/img2.jpg",
  "http://localhost:8000/images/img3.jpg",
  "http://localhost:8000/images/img4.jpg",
  "http://localhost:8000/images/img5.jpg",
  "http://localhost:8000/images/img6.jpg",
  "http://localhost:8000/images/img7.jpg",
  "http://localhost:8000/images/img8.jpg",
  "http://localhost:8000/images/img9.jpg",
  "http://localhost:8000/images/img10.jpg",
];

const imagesController = (req, res) => {
  const { page } = req.body;

  if (!page || isNaN(page)) {
    return res.status(400).send("Page number error");
  }

  const pageNumber = parseInt(page);
  const itemsPerPage = 5;
  const startIndex = (pageNumber - 1) * itemsPerPage;
  const endIndex = startIndex + itemsPerPage;

  if (startIndex >= imagePaths.length || pageNumber < 1) {
    return res.status(404).send("Page not found");
  }

  const imagesForPage = imagePaths.slice(startIndex, endIndex);

  const htmlResponse = `
      ${imagesForPage
        .map((img, index) => `Image${index}`)
        .join("\n")}
  `;

  res.send(htmlResponse);
};

expressRouter.post("/images", imagesController);

module.exports = expressRouter;

重要的是,我们要根据页面动态生成图像。另外,值得注意的是,图像的路径不会指向文件夹,而是指向地址本身。在 `app.js` 文件中,我们将执行此操作,以便从文件夹加载图像。

现在,这非常简单。当我们发出 `GET` 请求来获取标题时,我们将发送一个简单的 html 文件。代码如下所示:

**routes/get.js**

const express = require("express");
const expressRouter = express.Router();
const path = require("path");

const titleController = (req, res) => {
  res.sendFile(path.join(__dirname, "../components/GET/title.html"));
};

expressRouter.use("/title", titleController);

module.exports = expressRouter;

在 html 文件中,我们只需要一个包含文本的“span”,仅此而已。原则上,可以发出“POST”请求并为应用程序添加多语言功能,但这又会给应用程序带来负担。我想让它或多或少变得简单,但同时又美观且实用。

现在,剩下的就是将所有这些连接到一个文件中并启动我们的服务器。为此,让我们导入文件并创建一个 express 应用程序:

**应用程序.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");
const postRoutes = require("./routes/post");

app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors({ origin: true, credentials: true }));

const imagesFolder = path.join(__dirname, "./images");
app.use("/images", express.static(imagesFolder));

app.use(express.static(path.join(__dirname, "src")));

app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "src/index.html"));
});

app.use("/api", getRoutes);
app.use("/api", postRoutes);

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

在这里我们设置了“CORS”,以便我们可以从另一个“localhost”端口发送请求,并从文件夹加载图像。具体来说,我们在这里这样做:

const imagesFolder = path.join(__dirname, "./images");
app.use("/images", express.static(imagesFolder));

另外,您可以为服务器指定“PORT”,但我指定了默认的“8000”。您还需要配置“bodyParser”以方便使用 HTML,实际上,只需将路由连接到 api 即可。现在,我想您可以安全地使用该应用程序了!

结论

这个应用程序,即使看起来很小,考虑到只实现了最低限度的功能,也显得相当复杂。但是,这也很酷,因为有修改的空间、高质量的组装和简单的现代模块。你可以用 PHP 或其他语言实现后端,对客户端的意义不会有太大变化,所以我认为这个应用程序甚至可以作为一个宠物项目。

非常感谢大家阅读这篇文章!它看起来相当长,即使我试图在某些地方缩短叙述,在某些地方不涉及细节,但即使如此,它还是很多,但我希望它对你来说很有趣和有用!

项目存储库

该项目位于 GitHub 上。在那里您可以更详细地了解代码。将来可能会有其他相同甚至更酷的项目。

hmpl 语言/示例

HMPL 上的示例应用程序列表

HMPL 上的示例应用程序列表

本库包含使用 HMPL 模板语言编写的测试应用程序列表。您可以放心地拿走它们并进行修改(只需替换图片)。

图库应用程序

具有分页功能的花卉图库应用程序。图像以及应用程序本身的名称都是从服务器上传的。

Photo 1Photo 2Photo3在 GitHub 上查看Like

另外,如果您用您的星星支持**项目**,我会很高兴。谢谢!

星 HMPL ☆