HTML模板怎么写:从页面结构到样式拆分

HTML模板怎么写:从页面结构到样式拆分

HTML模板怎么写:从页面结构到样式拆分

HTML 模板不是某一种固定语法。它可以是一份可复用的静态页面骨架,也可以是带占位符、由服务端模板引擎渲染的文件,还可以是浏览器中的 <template> 元素。三者解决的问题相近:把内容结构、样式和数据组织起来,避免每次从头拼页面。但它们的运行环境和安全边界并不相同。

本文从一个简单页面开始,说明如何拆分结构与样式、如何插入动态数据,以及哪些场景需要额外注意。示例可保存为文件后在浏览器中运行;涉及 HTML 的示例都以转义形式展示,复制时需还原为实际标签。

一、先判断要写的是哪一种“模板”

开始写模板前,先明确页面由谁生成、数据从哪里来。常见情况有三类:

  • 静态 HTML 页面:浏览器直接打开文件或访问静态网站,页面内容已经写在 HTML 中。适合说明页、文档、活动页和原型。
  • 浏览器端模板:HTML 中预先写好结构,JavaScript 获取数据后填入页面。适合客户端列表、弹窗、卡片等重复片段。
  • 服务端模板:服务器使用模板引擎,将数据渲染为 HTML 后发给浏览器。具体语法、转义行为和配置取决于所用引擎,不能把一种引擎的语法直接套到另一种引擎中。

本文的完整示例使用标准 HTML 和浏览器原生 JavaScript,不依赖模板引擎或第三方库。若项目使用服务端模板,页面结构和 CSS 拆分原则仍然适用,但动态表达式应按该引擎的文档编写。

二、先搭页面结构,再填内容

HTML 负责表达内容的含义和组织关系。一个常见页面至少应有文档类型、根元素、字符编码、视口设置和标题。页面主体可以根据内容使用 header、nav、main、section、article 和 footer 等元素。

这些元素不是为了让代码看起来更复杂。语义明确的结构更容易维护,也有助于浏览器、辅助技术和搜索引擎理解页面。不要为了获得某种默认样式而选择元素:标题应使用标题元素,导航应表达导航,普通容器才使用 div。

下面是一份最小页面骨架。代码中的实体写法用于在本文中展示标签;实际保存文件时,应把 &lt; 和 &gt; 还原成尖括号。

&lt;!doctype html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
  &lt;meta charset="utf-8"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;title&gt;页面标题&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;header&gt;
    &lt;a href="/"&gt;站点名称&lt;/a&gt;
    &lt;nav aria-label="主导航"&gt;
      &lt;a href="/about"&gt;关于&lt;/a&gt;
    &lt;/nav&gt;
  &lt;/header&gt;

  &lt;main&gt;
    &lt;h1&gt;页面主标题&lt;/h1&gt;
    &lt;p&gt;页面的主要内容。&lt;/p&gt;
  &lt;/main&gt;

  &lt;footer&gt;
    &lt;p&gt;页脚信息&lt;/p&gt;
  &lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;

lang 用于声明页面语言;charset 指定字符编码,UTF-8 是常用选择;视口声明让移动设备按设备宽度布局。页面应有清晰的主标题,通常使用一个描述页面主题的 h1,其余标题按内容层级使用,不要仅为字号大小跳过层级。

三、用真实内容组织重复区块

模板常见的工作是重复展示卡片、列表项或导航项。应先确定每个区块表达的内容,再决定标签和字段。例如文章卡片可能包括标题、摘要和链接;导航项则是目的地链接,而不是一段普通文字。

字段名称要表达含义,结构不必为每一个字段额外套一层容器。可读性来自合理缩进、明确命名和稳定的层级,不来自标签数量。对重复内容,优先使用列表结构;若内容本来就是一组条目,ul 或 ol 往往比多个无关的 div 更准确。

四、把 CSS 从结构中拆出来

小型原型可以使用页面内的 style 区块,但项目页面通常适合把样式放到独立的 CSS 文件中。这样结构修改和视觉调整可以分别检查,多个页面也能复用同一份基础样式。

典型目录可以很简单:

project/
  index.html
  styles.css
  app.js

HTML 在 head 中引用样式文件:

&lt;link rel="stylesheet" href="styles.css"&gt;

CSS 代码只写视觉规则,不混入 HTML 标签。选择器应尽量从组件或页面结构出发,避免大范围依赖元素位置。比如为卡片设置明确的类名,比用“页面中第三个 div”定位稳定得多。

:root {
  color-scheme: light;
  font-family: system-ui, sans-serif;
  line-height: 1.6;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  color: #202124;
  background: #f5f6f8;
}

.site-header,
.site-main,
.site-footer {
  width: min(100% - 2rem, 52rem);
  margin-inline: auto;
}

.site-header {
  padding-block: 1rem;
  border-bottom: 1px solid #d8dce2;
}

.site-main {
  padding-block: 2rem;
}

.card {
  padding: 1rem;
  border: 1px solid #d8dce2;
  border-radius: 0.5rem;
  background: #fff;
}

.card h2 {
  margin-block-start: 0;
}

.card a {
  color: #1457b8;
}

.site-footer {
  padding-block: 1rem 2rem;
  color: #555;
}

类名如 site-header、card 应表达用途,而不是某个瞬时外观。例如 red-text 把内容和颜色绑在一起,颜色变化时名称就失真;warning 或 status-message 更适合表达组件含义。

样式拆分不等于所有 CSS 都必须拆成许多小文件。文件数量应符合项目规模和构建方式。小站点一份样式表可能足够;组件很多的应用可以按组件或功能拆分,再由构建工具处理。不要在没有维护需求时制造过度复杂的目录。

五、可直接运行的浏览器模板示例

下面的页面展示浏览器原生 template 元素。模板中的内容默认不会作为页面可见内容显示;JavaScript 克隆其内容,再把数据写入对应节点。将示例保存为 index.html,并在同一目录创建上一节的 styles.css,即可在现代浏览器中打开查看。

&lt;!doctype html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
  &lt;meta charset="utf-8"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;title&gt;文章列表&lt;/title&gt;
  &lt;link rel="stylesheet" href="styles.css"&gt;
  &lt;script src="app.js" defer&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;header class="site-header"&gt;
    &lt;a href="/"&gt;示例站点&lt;/a&gt;
  &lt;/header&gt;

  &lt;main class="site-main"&gt;
    &lt;h1&gt;文章列表&lt;/h1&gt;
    &lt;ul id="article-list" aria-label="文章"&gt;&lt;/ul&gt;
  &lt;/main&gt;

  &lt;template id="article-template"&gt;
    &lt;li class="card"&gt;
      &lt;h2&gt;&lt;a class="article-link"&gt;&lt;/a&gt;&lt;/h2&gt;
      &lt;p class="article-summary"&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/template&gt;

  &lt;footer class="site-footer"&gt;
    &lt;p&gt;示例页面&lt;/p&gt;
  &lt;/footer&gt;
&lt;/body&gt;
&lt;/html&gt;

以下 JavaScript 可保存为同目录的 app.js。示例数据故意包含类似标签的文本,用来观察它会作为普通文字显示,而不会被解析为 HTML。

const articles = [
  {
    title: "模板的基本结构",
    summary: "从页面骨架开始组织内容。",
    url: "/articles/template-basics"
  },
  {
    title: "&lt;script&gt;这只是文字&lt;/script&gt;",
    summary: "使用 textContent 写入文本,不把它当作标签执行。",
    url: "/articles/safe-text"
  }
];

const list = document.querySelector("#article-list");
const template = document.querySelector("#article-template");

for (const article of articles) {
  const fragment = template.content.cloneNode(true);
  const link = fragment.querySelector(".article-link");
  const summary = fragment.querySelector(".article-summary");

  link.textContent = article.title;
  link.href = article.url;
  summary.textContent = article.summary;

  list.append(fragment);
}

实际保存 JavaScript 文件时,第二条标题中的实体也可以写成普通字符串 <script>这只是文字</script>,因为 JavaScript 字符串本身不会把字符自动解析成 HTML。这里为了让代码片段在文章中安全展示,对小于号和大于号做了 HTML 转义。

defer 让外部脚本在文档解析完成后执行,因此脚本可以查询页面中的列表和模板。示例中的 URL 是站内相对路径。如果数据来自用户输入或外部接口,链接目标还需按应用需求验证,不能因为使用了 textContent 就认为所有字段都已安全。

六、动态数据的安全边界

模板安全的核心问题不是“数据是否可信”,而是数据将进入什么上下文。HTML 文本、HTML 属性、URL、JavaScript 字符串和 CSS 都有不同的解析规则。对某一种上下文有效的转义方式,不能不加区分地用于另一种上下文。

  • 插入普通文字:浏览器端优先使用 textContent 或表单控件的 value。它们把数据当作文本,不解析其中的标签。
  • 设置属性:使用 DOM 属性接口并限制可接受的属性和取值。不要将不可信字符串拼接进 HTML 属性源码。
  • 设置链接:仅做文本转义不能阻止危险协议。需要限制允许的 URL 形式或协议,并根据业务决定是否只接受站内路径。
  • 渲染富文本:若确实需要让用户提供格式化 HTML,应使用经过维护的 HTML 清洗方案,按允许的标签、属性和协议进行白名单过滤;不能仅靠字符串替换解决。
  • 服务端模板:确认模板引擎是否默认进行上下文相关的自动转义,以及是否存在关闭转义的写法。关闭自动转义不应作为日常插入数据的方法。

尤其要避免把动态数据拼成一段 HTML,再交给 innerHTML。当数据中包含事件属性、危险元素或可执行的 URL 时,可能导致跨站脚本攻击。若业务必须插入 HTML,应采用经过审查的清洗策略,并限定允许内容;“来源是数据库”或“只有内部用户能提交”都不等于内容安全。

安全处理需要结合上下文。例如,字符串作为 HTML 文本时应按 HTML 文本规则处理;作为属性值时要按属性上下文处理;作为 URL 时还需要协议和目标校验。通用的“转义一次就到处安全”并不存在。优先使用 DOM API 或模板引擎提供的安全输出机制,减少手工拼接。

七、参数限制该怎么理解

HTML 本身没有一个适用于所有模板文件、文本字段或属性值的统一最大长度规定。浏览器、服务器、数据库、模板引擎、代理和应用代码可能分别设置限制,具体数值应查阅对应产品文档或项目配置。不要凭经验编造一个“HTML 模板最多支持多少字符”的固定答案。

同样,浏览器端示例里的数组长度、标题长度和摘要长度没有自动业务上限。真实应用应根据产品需求在数据入口处设定限制,并在服务端进行校验;前端校验主要改善使用体验,不能替代服务端校验。长度按字符、字节还是其他单位计算,也要在系统各层保持一致。

处理大列表时,还要考虑渲染成本、网络传输和用户体验。可以采用分页、按需加载或虚拟列表等方案,但是否需要取决于实际数据量和目标设备。模板机制本身不会自动解决数据量过大造成的性能问题。

文件上传、富文本、链接、表单输入等场景也有各自的安全要求。模板负责呈现,不应被当成权限控制、数据校验或防攻击的替代品。权限判断和关键校验应放在可信的服务端逻辑中。

八、常见错误与修正方法

1. 把所有内容都写进一个巨大标签

结构层级过深会让样式选择器难以维护。先按内容关系组织语义元素,再只为确有样式或行为需求的区块增加类名。不要为了“组件化”给每个文本节点都套容器。

2. 用标题元素控制字号

h1 到 h6 表示标题层级,不是字号工具。外观由 CSS 控制;标题等级应反映内容结构,而不是当前视觉设计。

3. 在多个位置重复同一段样式

重复规则会导致页面状态不一致。可复用的视觉规则放在公共样式中,组件差异通过有意义的类或修饰类表达。若样式只服务于一个局部组件,避免把选择器扩散到无关区域。

4. 用固定宽度适配所有屏幕

固定宽度可能在窄屏溢出。常见做法是限制最大内容宽度,同时允许容器在视口变窄时缩小,例如示例中的 width: min(100% - 2rem, 52rem)。布局还应检查长文本、缩放和窄屏状态。

5. 把动态内容直接拼进 HTML

字符串拼接容易遗漏上下文规则,导致内容被当作标签或脚本解析。纯文本用 textContent;服务端使用默认转义机制;确需富文本时采用合适的清洗方案。

6. 认为前端验证足够

浏览器代码可以被绕过,客户端传来的值不能直接视为可信。关键字段长度、格式、权限和业务约束应在服务端重新验证。

7. 用空链接或点击事件模拟链接

导航目的地应使用带有效目标的 a 元素;执行操作通常使用 button。这样键盘操作和浏览器行为更符合用户预期。避免用 href="#" 充当没有目标的链接。

九、写完后按清单检查

  • 页面是否声明字符编码、语言、视口和有意义的标题?
  • 内容结构是否使用合适的语义元素,标题层级是否合理?
  • 重复区域是否有稳定的结构和清楚的类名?
  • 样式是否与结构分开,并在窄屏和文本变长时仍可使用?
  • 动态文本是否按文本处理,动态 URL 是否经过业务允许范围校验?
  • 若使用服务端模板,是否确认了实际引擎的转义规则和配置?
  • 业务参数是否在服务端按明确规则校验,而不是依赖浏览器默认限制?

写好 HTML 模板,关键不是套用某个固定目录或标签清单,而是先确定生成位置和数据边界,再用语义结构表达内容,把可复用视觉规则交给 CSS。静态页面可以保持简单;需要动态渲染时,应优先使用明确的数据接口和安全的输出方式。把这些边界处理清楚,模板才既易改,也不容易在功能增加后变成难以维护的字符串拼接代码。

© 版权声明

相关文章