简介:这套公司官网HTML页面模板面向具备基础前端知识、希望快速搭建科技类企业站点的开发者与学习者,提供可直接运行的静态网站骨架,涵盖首页、关于我们、产品服务、新闻中心等常见栏目页面。压缩包共48个文件,约1003KB,以18张jpg与5张png图片素材承担横幅、产品图与图标展示,4个html页面搭建主体结构,6个css文件负责配色、排版与多屏适配,15个js脚本支撑轮播、导航、弹窗、表单校验等交互,图片与代码分层存放,便于按模块替换内容。已有3459人学习,说明其作为企业站模板具备一定参考价值。借助完整的页面结构与图文素材,读者可对照理解HTML语义化标签、CSS3过渡动画与响应式布局的落地方式,并在现有样式基础上调整品牌色、栏目顺序与交互细节,快速改造成符合自身业务需求的官网雏形。
1. 从一次改版说起
去年帮一家做工业配件的客户改官网,设计稿前后改了四版,留给前端的排期只有三天。手里正好有一套公司官网 HTML 页面模板,语义化标签、CSS 自定义属性、四个响应式断点都是现成的,我把配色变量和文案换掉,第二天下午就挂到测试机上了。这类模板真正的价值不在于视觉多惊艳,而在于它把「首页—服务—案例—联系」这套公司站的标准骨架提前焊死,剩下的活变成换皮和填内容。它适合两类人:一是没有专职前端、又要自己维护官网的小团队;二是想拿一份干净的 HTML + CSS 工程练手、搞明白响应式布局到底怎么组织的初学者。下面按我拆这套模板的顺序写,从目录结构、页面骨架、样式架构一路讲到怎么改成自己公司的站。
2. 项目定位与技术选型:静态模板凭什么扛起一个官网
2.1 先把需求摊开:官网到底需要什么
公司官网的需求其实非常收敛:访客进来要知道你是干什么的、看过服务或产品、信任背书(客户 logo、案例、资质)、最后能联系上你。页面数量通常五到八页,内容更新频率按月甚至按季度算,不需要登录、不需要购物车、不需要实时数据。这种需求用 React 或 Vue 起一个 SPA,收益几乎为零,代价却是一整套构建链、依赖升级和 SEO 上的额外处理。
这套模板的取舍就是奔着这个场景去的。它提供的功能点包括:固定在顶部的响应式导航(移动端折叠成汉堡菜单)、首屏 Banner 加一句主张文案和一个主 CTA、服务/产品三列卡片区、客户 logo 横排、数字统计条、带校验的联系表单、四列页脚、返回顶部按钮。听上去朴素,但公司站 90% 的改版需求都在这几块里打转。
我一般把这类模板当成「结构参考 + 样式基线」两件事来看:结构参考是指 HTML 的分区怎么切、类名怎么起、可访问性属性放在哪;样式基线是指颜色、间距、字号、圆角、阴影这些设计令牌(design token)集中在哪里。前者决定你改内容顺不顺手,后者决定你换一次品牌色要动几个文件。一套模板好不好,看这两点比看截图准得多。
提示:拿到任何官网模板,先别急着改样式,把首页从头滚到尾,对照上面这份功能清单数一遍,缺哪块心里先有数。
2.2 技术栈边界:原生 HTML + CSS,加薄薄一层 JS
技术栈是刻意的「窄」:HTML5 语义化标签负责结构,CSS3 负责全部视觉与布局,JavaScript 只做三件浏览器原生做不到的事——汉堡菜单开合、滚动时给导航加阴影、表单提交前的本地校验。没有 jQuery,没有 Bootstrap 那套栅格,也没有打包工具。
为什么敢不用框架?因为官网的本质是内容型文档,交互密度极低。CSS 的 Grid 和 Flex 早就能覆盖绝大多数布局,clamp()能做流式字号,媒体查询能处理断点,这些能力放在 2024 年的浏览器里已经足够。用原生 HTML + CSS 写官网的直接好处是:双击就能预览,扔到任意静态托管上就能跑,三年后回来改一行文案不需要先npm install。
代价也要说清楚。第一,组件不能复用,五个页面里相同的导航栏就是五份拷贝,改一次要同步五处;第二,没有模板继承,页脚更新得逐页替换。如果站点超过十页、或者有编辑频繁更新内容的诉求,我一般会建议在保留这套 CSS 的前提下,把 HTML 迁到静态站点生成器上,把导航和页脚抽成 layout,构建产物依然是纯静态文件,部署方式不变。这是后话,第 6 章会讲具体路径。
2.3 目录结构:文件怎么摆,页面怎么拆
一个合格的官网模板,目录应该一眼能看出「资源在哪、页面在哪、样式怎么分」。下面是我拆过的这类模板里最常见也最好维护的一种摆法:
company-site/ ├── index.html # 首页 ├── about.html # 关于我们 ├── services.html # 服务/产品列表 ├── contact.html # 联系我们 ├── assets/ │ ├── css/ │ │ ├── reset.css # 浏览器默认样式归一 │ │ ├── main.css # 设计令牌 + 布局 + 组件样式 │ │ └── pages.css # 单页特有样式,按需引入 │ ├── js/ │ │ └── main.js # 导航、滚动、表单校验,约 100 行 │ ├── img/ │ │ ├── logo.svg │ │ ├── hero.webp │ │ └── clients/ # 客户 logo,统一尺寸 │ └── fonts/ # 自托管字体,可选 ├── favicon.ico └── README.md这套结构的逻辑是「按类型分层,按页面兜底」。reset.css单独拆出来,是因为它几乎永远不用改,混进main.css只会让后续 diff 变脏。main.css里放设计令牌、容器、按钮、卡片这类跨页面复用的东西,pages.css只放某一页独有的布局,比如联系页左侧地图右侧表单的两栏结构。页面文件全部平铺在根目录,是因为静态托管对相对路径最友好,放到子目录里反而容易在部署时踩路径的坑。
文件命名上,图片统一小写加连字符,hero-bg.webp而不是HeroBG.PNG。这条看着啰嗦,但线上服务器区分大小写,本地 Windows 或 macOS 默认不区分,本地跑得好好的页面传上去 404,我见过不止一次。客户 logo 单独开一个目录,是为了统一按 160×60 这类固定尺寸导出,避免页面上 logo 高矮不齐——这是官网最容易被挑刺的地方。
3. 从本地跑通到页面骨架:一个请求进来看到的全貌
3.1 起一个静态服务,别直接双击 HTML
很多人拿到模板的第一反应是双击index.html,浏览器地址栏变成file:///...。首页大概率能看,但只要涉及fetch拉数据、字体跨域、或者用绝对路径引资源,就会莫名其妙失败。正确姿势是在项目根目录起一个静态服务器:
# 方式一:Python 自带,几乎每台机器都有 python3 -m http.server 8080 # 方式二:Node 环境,适合后续要接构建工具的情况 npx serve . -l 8080 # 方式三:VS Code 装 Live Server 插件,右键 Open with Live Server第一条命令的含义是把当前目录作为网站根目录,监听 8080 端口,浏览器访问http://localhost:8080即可。它会把/映射到index.html,把assets/css/main.css这类相对路径正确解析成文件请求——这跟线上 Nginx 的行为一致,所以本地看到的效果基本就是上线后的效果。第二条命令的-l参数指定端口,端口被占用时换一个就行,别死磕。
跑起来之后别急着改代码,先做三件事:把窗口从桌面宽度拖到手机宽度,看断点切换是否平滑;打开开发者工具的 Network 面板刷新一次,看有没有 404 的资源;切到 Console 看有没有报错。这三步五分钟,能提前排掉一大半后面要花两小时查的问题。
3.2 index.html 的骨架:语义化分区怎么划
首页是整个模板信息密度最高的一页,它的结构决定了后面所有内页怎么照抄。下面是我惯用的骨架,去掉了具体文案,只留结构:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>某某公司 | 工业配件与整体解决方案</title> <meta name="description" content="某某公司专注工业配件研发与供应,提供选型、定制与售后一站式服务。"> <link rel="preload" href="assets/fonts/inter.woff2" as="font" type="font/woff2" crossorigin> <link rel="stylesheet" href="assets/css/reset.css"> <link rel="stylesheet" href="assets/css/main.css"> </head> <body> <!-- 跳过导航,键盘和读屏用户可直接跳到主内容 --> <a class="skip-link" href="#main">跳到主要内容</a> <header class="site-header" id="siteHeader"> <nav class="nav container" aria-label="主导航"> <a class="nav__logo" href="index.html">某某公司</a> <button class="nav__toggle" aria-expanded="false" aria-controls="navMenu"> <span class="sr-only">展开菜单</span> </button> <ul class="nav__menu" id="navMenu"> <li><a href="index.html" aria-current="page">首页</a></li> <li><a href="services.html">服务</a></li> <li><a href="about.html">关于</a></li> <li><a href="contact.html">联系</a></li> </ul> </nav> </header> <main id="main"> <section class="hero" aria-labelledby="heroTitle">...</section> <section class="services" aria-labelledby="servicesTitle">...</section> <section class="stats">...</section> <section class="cta">...</section> </main> <footer class="site-footer">...</footer> <script src="assets/js/main.js" defer></script> </body> </html>几个地方值得单独说。lang="zh-CN"不是可选项,它影响读屏软件的发音和中文字体的默认渲染,漏掉之后有些浏览器会把中文当日文处理,字形会歪。viewport那行是移动端适配的前提,缺了它,后面写再多媒体查询手机上都是缩小版的桌面页。
<header>、<main>、<footer>、<section>这些语义标签不是装饰,它们给搜索引擎和辅助技术提供了页面分区信息。每个<section>挂aria-labelledby指向自己的标题 id,读屏用户跳转区域时能听到「服务区」而不是一片空白。
<script>放在</body>前并加defer,是为了不阻塞 HTML 解析。defer表示脚本下载完等 DOM 解析结束再按顺序执行,比放在<head>里裸奔或监听DOMContentLoaded都干净。
skip-link那个小链接平时被 CSS 藏在视口外,Tab 聚焦时才出现,是官网可访问性检查里最便宜的一个加分项,加上不亏。
3.3 导航与首屏:最容易被改坏的两块
导航栏是整站复用度最高、也最容易改出问题的组件。它的 CSS 核心是三段:桌面横向排布、移动端隐藏菜单、展开时显示。
.site-header { position: sticky; top: 0; z-index: 100; background: rgba(255, 255, 255, .92); backdrop-filter: blur(8px); transition: box-shadow .2s ease; } /* JS 滚动超过 8px 时加上这个类 */ .site-header.is-scrolled { box-shadow: 0 2px 12px rgba(20, 30, 60, .08); } .nav { height: 68px; display: flex; align-items: center; justify-content: space-between; } .nav__menu { display: flex; gap: 2rem; } /* 移动端:菜单折叠成竖排浮层 */ @media (max-width: 767px) { .nav__toggle { display: block; } .nav__menu { position: absolute; top: 68px; left: 0; right: 0; flex-direction: column; gap: 0; padding: 1rem 1.25rem; background: #fff; border-top: 1px solid var(--color-border); display: none; /* 默认收起 */ } .site-header.is-open .nav__menu { display: flex; } }position: sticky比fixed好在不脱离文档流,不用给body补 padding,省一个坑。backdrop-filter做毛玻璃效果在 Safari 上要加-webkit-前缀,老版本才认。.is-open和.is-scrolled这两个状态类由 JS 切换,CSS 只负责「有类长什么样」,职责分得干净。
首屏 Hero 区的问题通常出在高度和图片上。我一般不用height: 100vh,因为手机浏览器的地址栏会收缩,100vh在 iOS 上会导致内容被裁;换成min-height: min(88vh, 720px)更稳,再配一张object-fit: cover的背景图。
.hero { min-height: min(88vh, 720px); display: grid; place-items: center; text-align: center; padding: 6rem 1.25rem; background: linear-gradient(rgba(10, 18, 40, .55), rgba(10, 18, 40, .55)), url("../img/hero.webp") center / cover no-repeat; } .hero__title { font-size: clamp(1.9rem, 4vw, 3.2rem); line-height: 1.15; color: #fff; }clamp(最小值, 弹性值, 最大值)让标题字号随视口在 1.9rem 到 3.2rem 之间线性变化,中间不需要写任何媒体查询,这是近几年做响应式排版最省事的写法。渐变叠在图片上是为了让白色文字在任何底图上都有对比度,rgba的透明度 0.55 是个经验值,压到 0.4 以下白字就开始发飘。
3.4 服务卡片与联系表单:可复用区块的写法
服务区通常是三列卡片,每张卡片一个图标、一个标题、一段描述、一个「了解更多」链接。这里用 Grid 比 Flex 更合适,因为列数固定、需要对齐:
<section class="services container" aria-labelledby="servicesTitle"> <h2 id="servicesTitle" class="section__title">我们提供什么</h2> <div class="services__grid"> <article class="card"> <img class="card__icon" src="assets/img/icon-design.svg" alt="" width="40" height="40"> <h3 class="card__title">方案设计</h3> <p class="card__desc">根据产线工况提供选型与配套方案,72 小时内出初稿。</p> <a class="card__link" href="services.html#design">了解更多</a> </article> <!-- 其余卡片结构一致,改文案即可 --> </div> </section>图标那行alt=""是故意的:图标右侧紧跟着文字标题,读屏时重复念一遍反而吵,空 alt 让它被跳过。width和height属性必须写,浏览器能提前按比例留出位置,避免图片加载完成后页面跳一下——这个跳动在官网首屏尤其影响体验。
联系表单的 HTML 部分重点是label和input的绑定,以及required、type="email"这些原生校验属性:
<form class="contact__form" novalidate> <div class="field"> <label for="name">称呼</label> <input id="name" name="name" type="text" required autocomplete="name"> <p class="field__error">:root { /* 品牌色:改这两个值就等于换皮 */ --color-primary: #1f5eff; --color-primary-hover: #164ad1; /* 中性色阶 */ --color-text: #1a1d23; --color-muted: #5b6472; --color-bg: #ffffff; --color-surface: #f5f7fb; --color-border: #e3e7ee; /* 间距:4px 基准的等比阶梯 */ --space-1: .5rem; /* 8px */ --space-2: 1rem; /* 16px */ --space-3: 1.5rem; /* 24px */ --space-4: 2.5rem; /* 40px */ --space-5: 4rem; /* 64px */ --radius-sm: 6px; --radius-md: 10px; --shadow-card: 0 6px 24px rgba(20, 30, 60, .08); --container: 1160px; --font-sans: "Inter", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; }命名规则是「--类别-语义-变体」:--color-primary是类别加语义,--space-3是类别加序号。不要用--blue-500这种描述色值的名字,因为品牌色一换,「blue」里装的可能是绿色,看着别扭还容易改错。间距用序号而不是像素值,是为了让全站节奏统一——所有卡片内边距都取--space-3,视觉上自然对齐,不用挨个调。
使用侧就一行:
.btn--primary { background: var(--color-primary); color: #fff; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); font: 600 1rem/1.2 var(--font-sans); } .btn--primary:hover { background: var(--color-primary-hover); }字号那行用了font简写,顺序是「字重 / 字号 / 行高 / 字族」,行高不写单位表示字号的倍数。这种写法紧凑,但团队成员不熟的话可读性差,团队协作场景里我一般还是拆成四行。
4.2 布局选择:Flex 和 Grid 的分工
新手最容易纠结的是「这块该用 Flex 还是 Grid」。我的判断标准很直白:一维排列用 Flex,二维对齐用 Grid。导航栏、按钮组、卡片内部的图标加文字,都是沿一个方向排,Flex 足够;服务卡片区、页脚的四列、图片墙这种行列都要控制的,Grid 更省事。
以服务区为例:
.services__grid { display: grid; gap: var(--space-3); grid-template-columns: 1fr; /* 移动端:单列 */ } @media (min-width: 768px) { .services__grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { .services__grid { grid-template-columns: repeat(3, 1fr); } }这里有个比repeat(3, 1fr)更现代的写法,可以让列宽自适应而不写断点:
.services__grid { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }auto-fit表示能塞几列塞几列,minmax(280px, 1fr)表示每列最窄 280px、最宽平分剩余空间。容器宽度变化时列数自动增减,桌面三列、平板两列、手机一列,一行搞定。注意auto-fit和auto-fill的区别:前者在项目少时会把空轨道合并、让卡片撑满整行,后者会保留空轨道导致卡片挤在左边。官网卡片数量固定,用auto-fit视觉更饱满。
卡片的等高问题是另一个高频翻车点。Grid 的单元格默认stretch,卡片自然等高,但如果卡片内部用的是 Flex 纵向排列,得补一句.card { display: flex; flex-direction: column; }再给链接加margin-top: auto,才能让「了解更多」始终贴底对齐,而不是随着描述文字长短上下浮动。
4.3 响应式断点:移动优先怎么写
模板采用移动优先:默认样式是手机版,媒体查询统一用min-width往上加。断点取三个值——768px、1024px、1280px,分别对应平板竖屏、平板横屏和桌面。
/* 默认:手机 */ .container { padding-inline: 1.25rem; } /* 平板 */ @media (min-width: 768px) { .container { padding-inline: 2rem; max-width: var(--container); margin-inline: auto; } } /* 桌面 */ @media (min-width: 1024px) { .section { padding-block: var(--space-5); } }为什么坚持移动优先而不是桌面优先?因为手机版的样式最简单,通常是单列、字号小、间距紧,往上加规则时是「增加」,逻辑是累加的;桌面优先反过来要用max-width一层层减,规则互相覆盖,改到第三个断点就理不清哪个属性最终生效。padding-inline和margin-inline是逻辑属性,写起来比padding-left/right短,而且天然支持从右往左的语言,虽然官网用不上,但习惯养成了不亏。
断点值不要照搬框架的 576/768/992/1200 一套,那是通用值。实际项目里更实用的做法是:把浏览器拖窄,看内容在哪一个宽度开始变丑,那个宽度就是你的断点。内容驱动断点比设备驱动断点靠谱得多。
4.4 那点 JS:汉堡菜单、滚动阴影、表单校验
main.js一共百来行,分三块。第一块是导航开合:
const header = document.getElementById('siteHeader'); const toggle = document.querySelector('.nav__toggle'); const menu = document.getElementById('navMenu'); toggle.addEventListener('click', () => { const isOpen = header.classList.toggle('is-open'); // aria-expanded 必须同步,否则读屏用户不知道菜单状态 toggle.setAttribute('aria-expanded', String(isOpen)); }); // 点击菜单里的链接后,移动端自动收起 menu.addEventListener('click', (e) => { if (e.target.tagName === 'A' && window.innerWidth < 768) { header.classList.remove('is-open'); toggle.setAttribute('aria-expanded', 'false'); } });classList.toggle返回布尔值表示切换后是否存在该类,直接拿来同步aria-expanded,省一次contains判断。第二段监听滚动给导航加阴影,注意加了{ passive: true },告诉浏览器这个监听不会调preventDefault,滚动性能能好一截:
let ticking = false; window.addEventListener('scroll', () => { if (ticking) return; ticking = true; requestAnimationFrame(() => { header.classList.toggle('is-scrolled', window.scrollY > 8); ticking = false; }); }, { passive: true });用requestAnimationFrame节流,是因为滚动事件触发频率极高,直接在里面改 class 会让主线程忙于样式计算。ticking这个标记保证一帧内只处理一次。第三段是表单校验:
const form = document.querySelector('.contact__form'); form.addEventListener('submit', async (e) => { e.preventDefault(); const data = Object.fromEntries(new FormData(form).entries()); const errors = {}; if (!data.name || data.name.trim().length < 2) errors.name = '请填写至少 2 个字的称呼'; if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email || '')) errors.email = '邮箱格式看起来不对'; if (!data.message || data.message.trim().length < 10) errors.message = '需求描述至少 10 个字'; // 先清空旧错误 form.querySelectorAll('.field__error').forEach((el) => { el.textContent = ''; }); if (Object.keys(errors).length) { Object.entries(errors).forEach(([field, msg]) => { const el = form.querySelector(`[data-error-for="${field}"]`); if (el) el.textContent = msg; }); return; } // 校验通过,交给后端或表单服务 await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); form.reset(); });Object.fromEntries(new FormData(form).entries())一行把表单转成普通对象,比逐个querySelector取值干净。邮箱正则用的是最常见的宽松版,只拦明显不合法的输入,别追求 RFC 完全匹配——那种正则又长又难维护,真正的有效性验证只能靠发确认邮件。
5. 避坑与排查:静态模板最容易翻车的六处
5.1 样式类问题:本地好好的,换台机器就变形
现象一:设计稿上是思源黑体,同事的 Windows 电脑打开变成宋体。原因多半是font-family只写了"PingFang SC"这类 mac 才有的字体,Windows 找不到就回落到默认衬线字体。解决办法是补全字体栈并用系统字体兜底:font-family: "Inter", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;。system-ui会让浏览器直接调用操作系统的 UI 字体,比硬编码字体名可靠。如果要严格统一,就把字体文件放进assets/fonts/自托管,用@font-face声明并加font-display: swap,避免文字长时间不可见。
现象二:三张服务卡片高度参差不齐,底部链接高低错落。原因通常是卡片用了display: inline-block或者浮动,或者虽然用 Grid 但卡片内部的链接没有贴底。解决分两步:外层 Grid 保证单元格等高,内层卡片改display: flex; flex-direction: column;,再给「了解更多」加margin-top: auto;。这一句的作用是把剩余空间全部分配到链接上方,无论描述文字几行,链接都贴底。
现象三:手机上能左右滑动,页面出现横向滚动条。这是官网最常见的问题,成因有好几种:某个元素宽度超过视口、负margin把内容推出去了、100vw在带滚动条的浏览器上比可视宽度大。排查方法是打开开发者工具,在 Console 里跑一段脚本找出越界的元素:
// 找出所有右边界超出视口的元素 [...document.querySelectorAll('*')].filter((el) => { const r = el.getBoundingClientRect(); return r.right > window.innerWidth + 1 || r.left < -1; }).forEach((el) => console.log(el, el.getBoundingClientRect()));定位到具体元素后,优先级顺序是:改元素自身的宽度约束 > 给父容器加overflow-x: hidden。后者是止血,前者才是治本,因为overflow: hidden只是把溢出内容裁掉,元素实际位置还是错的。
5.2 移动端与交互问题:真机上和模拟器不一样
现象四:手机上点汉堡按钮没反应,或者点了菜单展开但页面不滚动。前者的典型原因是按钮被其他元素盖住了——导航栏用了position: sticky加z-index,而某个 Banner 的z-index更高,把点击事件吃掉了。在开发者工具里用元素选择器点一下按钮,看命中的是不是预期元素,一秒就能确认。后者的原因通常是菜单展开时给body加了overflow: hidden但忘记在关闭时移除,或者用了position: fixed的遮罩层没清理。建议把开合逻辑收在一个函数里成对处理,别在多个事件里散着改body样式。
现象五:iPhone 上点输入框,页面自动放大。原因是输入框的font-size小于 16px,iOS Safari 出于可读性会自动缩放视口。解决办法是把移动端输入框字号设为font-size: 16px——注意不能用1rem图省事,如果根字号被设成 14px,1rem依然是 14px,照样触发。这个坑很隐蔽,因为安卓上没有这个行为,只在真机 iOS 上复现,模拟器开到 iPhone 尺寸也未必能看出来。
注意:涉及移动端交互的改动,一定要在真机上过一遍。模拟器只解决布局问题,触摸、滚动、键盘弹起这些行为模拟不准。
5.3 上线与资源问题:本地 200,线上 404
现象六:首页能打开,但 CSS 和图片全部 404,页面变成纯文本。头号原因是路径大小写不一致,本地文件系统不区分,Linux 服务器区分。前面提过HeroBG.PNG引成hero-bg.png这类问题。次号原因是用了以/开头的绝对路径,本地起服务时根目录就是项目目录所以正常,部署到子路径(比如https://example.com/site/)之后就全错。排查方式是打开 Network 面板,看 404 请求的完整 URL 和实际文件路径差在哪一段;解决方式是全部改成相对路径,或者在构建时统一加base前缀。
现象七:首屏图片很大,页面白屏两三秒才出来。官网首屏那张 Banner 图经常是设计给的 4K 原图,三四兆。处理方式是转成 WebP 并按最大展示尺寸压到 200KB 以内,<img>上加loading="lazy"——但首屏图不要加 lazy,它本来就要立刻显示,加了反而延迟。非首屏图片再加loading="lazy"和decoding="async"让浏览器异步解码。字体也可以先preload,但只 preload 真正用到的那一个字重,多 preload 几个反而抢带宽。
6. 二次开发:把模板改成自己公司的站
拿到模板之后,我一般按四步走,顺序别乱:先换皮,再换内容,然后接表单,最后做上线前的收尾。
| 步骤 | 动作 | 主要改动位置 | 验证方式 |
|---|---|---|---|
| 换皮 | 改品牌色、字体、圆角 | main.css的:root | 全站扫一遍看有无色值硬编码残留 |
| 换内容 | 文案、图片、logo | 各.html文件、assets/img/ | 逐页对照需求清单勾选 |
| 接表单 | 对接后端接口或表单服务 | main.js的 fetch 地址 | 提交一次看服务端是否收到 |
| 收尾 | SEO、favicon、404 页 | <head>、根目录 | 用 Lighthouse 跑一遍 |
换皮那步的关键是「只改变量,不改规则」。搜索一下 CSS 里有没有漏网的十六进制色值,如果有,把它们也收进:root:
:root { /* 换成自己品牌色,只需改这两行 */ --color-primary: #c2410c; --color-primary-hover: #9a3412; }改完刷新,按钮、链接、图标、焦点框应该整体变色。如果某处没变,说明那个组件写死了颜色,回去补成变量。这也是我判断一套模板质量的方式:能靠两个变量换完整的品牌色,样式架构就算合格。
表单那步,fetch('/api/contact')里的地址换成自己的接口。没有后端的话,接一个第三方表单服务,把fetch换成对应服务的提交地址即可,前端校验逻辑保留——它拦掉的是明显填错的输入,能省掉不少垃圾提交。多页面扩展就更直接了,复制一份index.html,把<main>里的内容换掉,导航和页脚保留,aria-current="page"移到当前页对应的链接上。
提示:每加一个新页面,就把导航里那个
aria-current="page"挪一次,漏掉的话读屏用户就不知道自己在哪一页了。
最后说个我自己的教训。早年改这类模板,我习惯打开浏览器看首页没问题就直接传服务器,结果内页的导航高亮、表单页的样式、手机上菜单的收起,全是在上线后由客户截图告诉我的。从那以后我每次改完都强制走一遍清单:桌面宽度滚一遍所有页面、拖到 375px 宽再滚一遍、Tab 键把导航和表单走一遍、提交一次表单确认有返回。这四步大概八分钟,比上线后被返工省事得多。希望帮到你。
本文还有配套的精品资源,点击获取