编写种子搜索结果页面:公共模板与组件拆解实践

2026-02-19 66 浏览 0 评论

在上一篇文章中,我们已经完成了种子搜索结果页的整体结构和核心交互逻辑。但在一个稍微成规模的项目中,仅靠一个模板文件显然是不够的。

为了提高 代码复用性、可维护性和可读性 ,我们通常会将页面中可复用、职责单一的部分拆分成多个公共模板。本篇文章将围绕以下几个 EJS 文件,逐一讲解它们的设计思路与实现细节:

  • loop.ejs :种子列表项模板
  • pageNavi.ejs ​:分页组件
  • header.ejs :页面头部与导航
  • footer.ejs :页面底部
  • head.ejs :HTML 头部公共配置

一、loop.ejs:种子信息循环体

loop.ejs 是搜索结果列表中最核心的模板之一,用来描述 单条种子记录的展示结构

<div class="torItem">
  <h3>
    <input class="torItemCheck" value="<%-torrent.id%>" v-if="checkShow" type="checkbox" />
    <a href="/torrent/<%-torrent.hash%>"><%-torrent.name%></a>
  </h3>
  <div class="torFiles">
    <% torrent.files.forEach(function(file){ %>
    <p>
      <span class="torFileName"><%-file.name%></span>
      <span class="torSize"><%-ctx.helper.formatBytes(file.length)%></span>
    </p>
    <% }) %>
  </div>
  <div class="torMeta">
    <span v-if="!checkShow"><a href="magnet:?xt=urn:btih:<%-torrent.hash%>">磁力</a></span>
    <span>大小:<%-ctx.helper.formatBytes(torrent.length)%></span>
    <span>收录时间:<%-ctx.helper.date('Y-m-d H:i', torrent.add_date)%></span>
    <span>文件数:<%-torrent.file_count%></span>
    <span>热度:<%-torrent.download_count+1%>℃</span>
    <span><a href="javascript:" class="deleteTorrent" data-id="<%-torrent.id%>">删除</a></span>
  </div>
</div>

设计要点解析

服务端数据直出

  • 种子名称、文件列表、时间、大小等全部由服务端渲染
  • 有利于 SEO,也减少前端状态管理复杂度

Vue 与 EJS 的协作

  • v-if="checkShow" 控制是否显示复选框
  • Vue 不生成 DOM,只控制 显示与否 ,非常轻量

工具方法统一格式化

  • ctx.helper.formatBytes :统一文件大小显示
  • ctx.helper.date :统一时间格式
  • 避免在模板中写逻辑判断或字符串拼接

磁力链接的合理隐藏

  • 编辑模式下隐藏磁力链接,避免误操作
  • 普通模式下直接暴露,提升使用效率

二、分页组件:经典而稳定的实现方式

分页是搜索结果页中必不可少的一部分,这里采用的是 服务端分页 + URL 参数驱动 的方式。

<ul class="pageNavi">
  <li>共 <%-maxPage%> 页</li>
  <!-- 上一页 -->
  <% if(page>1){ %>
  <li>
    <a class="pageNavi-t" href="<%-ctx.helper.handleUrlParams(ctx.request.url, {page:page-1})%>">上一页</a>
  </li>
  <% } %>
  <!-- 中间循环的 -->
  <% pageArray.forEach(function(item, index){ %>
  <li>
    <a
      class="pageNavi-t <%-item==page?'current':'' %>"
      href="<%-ctx.helper.handleUrlParams(ctx.request.url, {page:item})%>"
    >
      <%-item%>
    </a>
  </li>
  <% }) %>
  <!-- 下一页 -->
  <% if(maxPage>page){ %>
  <li>
    <a class="pageNavi-t" href="<%-ctx.helper.handleUrlParams(ctx.request.url, {page:Number(page)+1})%>">下一页</a>
  </li>
  <% } %>

分页设计思路

  • 分页状态完全由 URL 决定
  • 切换页码不会丢失搜索条件、排序方式
  • handleUrlParams 统一处理参数拼接,避免手写字符串错误
  • pageArray 由服务端控制页码范围,防止前端逻辑过重

每页条数切换

<select class="form-control" id="pageNaviSelect">
  <option value="10" :selected="<%-ctx.query.rows%> == 10">每页 10 条</option>
  <option value="20" :selected="<%-ctx.query.rows%> == 20">每页 20 条</option>
  <option value="50" :selected="<%-ctx.query.rows%> == 50">每页 50 条</option>
  <option value="100" :selected="<%-ctx.query.rows%> == 100">每页 100 条</option>
</select>

这种方式简单直接,也非常符合传统搜索网站的用户习惯。


三、header.ejs:导航与搜索入口

header.ejs 负责站点顶部导航和全局搜索入口,是 用户使用频率最高的组件之一

<header>
  <nav class="navbar navbar-default navbar-fixed-top">
    ...

核心功能点

  1. 导航高亮
   <li class="<%-ctx.request.url.split('?')[0]=='/'?'active':''%>">
  • 根据当前 URL 自动设置 active 状态
  • 无需前端参与,逻辑直观
  1. 全局搜索框
   <form action="/search">
  • GET 请求,天然支持 URL 分享
  • 搜索关键词回填,增强体验
  1. 登录状态判断
   <% if(ctx.session.user.id){ %>
  • 服务端判断用户状态
  • 前端无需关心权限问题

四、footer.ejs:统一资源引入与版权信息

<footer>
  <div class="copyright">
    Copyright &copy; <%-ctx.helper.date('Y')%> 磁力虫 All rights reserved.
  </div>
</footer>
<script src="/public/js/jquery.min.js"></script>
<script src="/public/js/bootstrap.min.js"></script>
<script src="/public/js/clipboard.min.js"></script>
<script src="/public/js/vue.min.js"></script>
<script src="/public/js/script.js"></script>

这样设计的好处

  • JS 统一放在底部,避免阻塞渲染
  • 公共脚本集中管理,方便维护
  • 页面模板本身更干净

五、head.ejs:HTML 头部公共配置

<head>
  <title><%-title%></title>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" type="text/css" href="/public/css/bootstrap.min.css" />
  <link rel="stylesheet" type="text/css" href="/public/css/style.css" />
  <script src="/public/js/vue.js"></script>
</head>

设计说明

  • title 由服务端动态注入,利于 SEO
  • Bootstrap + 自定义样式分层清晰
  • Vue 在 head 中提前引入,方便模板内直接使用指令

六、总结

通过这两个章节,我们实际上完成了一套 完整、可复用、工程化程度较高的搜索结果页模板体系

  • 页面结构清晰
  • 模板职责单一
  • 服务端与前端边界明确
  • 非常适合中小型内容站点或工具类网站

这种 EJS + Vue + jQuery 的组合,在今天看来并不过时,反而在很多 非 SPA 场景 中依然是性价比极高的选择。

在下一步,你可以继续优化的方向包括:

  • 模板进一步组件化
  • 权限与操作按钮控制
  • 前后端渲染边界收敛
  • 搜索结果缓存与性能优化

希望这篇文章能对你理解和组织服务端模板结构有所帮助。


发布评论

发布评论前请先 登录
取消
0 评论
点赞
收藏

评论列表 0

暂无评论