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

HTML模板怎么写:从页面结构到样式拆分
HTML 模板不是某一种固定语法。它可以是一份可复用的静态页面骨架,也可以是带占位符、由服务端模板引擎渲染的文件,还可以是浏览器中的 <template> 元素。三者解决的问题相近:把内容结构、样式和数据组织起来,避免每次从头拼页面。但它们的运行环境和安全边界并不相同。
本文从一个简单页面开始,说明如何拆分结构与样式、如何插入动态数据,以及哪些场景需要额外注意。示例可保存为文件后在浏览器中运行;涉及 HTML 的示例都以转义形式展示,复制时需还原为实际标签。
一、先判断要写的是哪一种“模板”
开始写模板前,先明确页面由谁生成、数据从哪里来。常见情况有三类:
- 静态 HTML 页面:浏览器直接打开文件或访问静态网站,页面内容已经写在 HTML 中。适合说明页、文档、活动页和原型。
- 浏览器端模板:HTML 中预先写好结构,JavaScript 获取数据后填入页面。适合客户端列表、弹窗、卡片等重复片段。
- 服务端模板:服务器使用模板引擎,将数据渲染为 HTML 后发给浏览器。具体语法、转义行为和配置取决于所用引擎,不能把一种引擎的语法直接套到另一种引擎中。
本文的完整示例使用标准 HTML 和浏览器原生 JavaScript,不依赖模板引擎或第三方库。若项目使用服务端模板,页面结构和 CSS 拆分原则仍然适用,但动态表达式应按该引擎的文档编写。
二、先搭页面结构,再填内容
HTML 负责表达内容的含义和组织关系。一个常见页面至少应有文档类型、根元素、字符编码、视口设置和标题。页面主体可以根据内容使用 header、nav、main、section、article 和 footer 等元素。
这些元素不是为了让代码看起来更复杂。语义明确的结构更容易维护,也有助于浏览器、辅助技术和搜索引擎理解页面。不要为了获得某种默认样式而选择元素:标题应使用标题元素,导航应表达导航,普通容器才使用 div。
下面是一份最小页面骨架。代码中的实体写法用于在本文中展示标签;实际保存文件时,应把 < 和 > 还原成尖括号。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>页面标题</title>
</head>
<body>
<header>
<a href="/">站点名称</a>
<nav aria-label="主导航">
<a href="/about">关于</a>
</nav>
</header>
<main>
<h1>页面主标题</h1>
<p>页面的主要内容。</p>
</main>
<footer>
<p>页脚信息</p>
</footer>
</body>
</html>
lang 用于声明页面语言;charset 指定字符编码,UTF-8 是常用选择;视口声明让移动设备按设备宽度布局。页面应有清晰的主标题,通常使用一个描述页面主题的 h1,其余标题按内容层级使用,不要仅为字号大小跳过层级。
三、用真实内容组织重复区块
模板常见的工作是重复展示卡片、列表项或导航项。应先确定每个区块表达的内容,再决定标签和字段。例如文章卡片可能包括标题、摘要和链接;导航项则是目的地链接,而不是一段普通文字。
字段名称要表达含义,结构不必为每一个字段额外套一层容器。可读性来自合理缩进、明确命名和稳定的层级,不来自标签数量。对重复内容,优先使用列表结构;若内容本来就是一组条目,ul 或 ol 往往比多个无关的 div 更准确。
四、把 CSS 从结构中拆出来
小型原型可以使用页面内的 style 区块,但项目页面通常适合把样式放到独立的 CSS 文件中。这样结构修改和视觉调整可以分别检查,多个页面也能复用同一份基础样式。
典型目录可以很简单:
project/
index.html
styles.css
app.js
HTML 在 head 中引用样式文件:
<link rel="stylesheet" href="styles.css">
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,即可在现代浏览器中打开查看。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>文章列表</title>
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
</head>
<body>
<header class="site-header">
<a href="/">示例站点</a>
</header>
<main class="site-main">
<h1>文章列表</h1>
<ul id="article-list" aria-label="文章"></ul>
</main>
<template id="article-template">
<li class="card">
<h2><a class="article-link"></a></h2>
<p class="article-summary"></p>
</li>
</template>
<footer class="site-footer">
<p>示例页面</p>
</footer>
</body>
</html>
以下 JavaScript 可保存为同目录的 app.js。示例数据故意包含类似标签的文本,用来观察它会作为普通文字显示,而不会被解析为 HTML。
const articles = [
{
title: "模板的基本结构",
summary: "从页面骨架开始组织内容。",
url: "/articles/template-basics"
},
{
title: "<script>这只是文字</script>",
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。静态页面可以保持简单;需要动态渲染时,应优先使用明确的数据接口和安全的输出方式。把这些边界处理清楚,模板才既易改,也不容易在功能增加后变成难以维护的字符串拼接代码。









