Performance Playbook

页面加载与打开速度优化经验手册

这份文档沉淀本次对 yjxxdesign.com 的移动端、PC 端、视频、图片、Vue 模板闪烁、nginx 静态资源策略、部署验证等优化经验。目标是以后迁移到其他网站时,能快速复用诊断方法、代码片段和上线检查流程。

视频加载 图片缩略图 Cloudflare 重资源分流 Vue 模板防闪烁 nginx 免费优化 发布验证清单

1. 背景与核心结论

本次问题表现为:同一个项目在 yjxx.cloud 访问速度较好,而 yjxxdesign.com 在手机端和 PC 端打开慢,尤其是首页视频、hardware 图片、模块页初始渲染时偶尔出现模板乱码。

根因一:重资源来源不同

yjxx.cloud 使用 Cloudflare,视频和静态资源传输更稳;yjxxdesign.com 使用腾讯云源站加 nginx,在国内访问 HTML 没问题,但大视频和大量原始图片容易拖慢首屏。

根因二:页面直接加载原始资源

首页 PC 端曾直接加载 12MB-14MB 视频,hardware PC 页卡片直接加载 1MB-2MB 原始 JPG。对列表页来说,这些文件远超必要尺寸。

根因三:Vue 挂载前模板外露

PC 和移动端的模块页、硬件页都是独立 HTML + Vue 模板。Vue JS 未挂载前,浏览器可能先显示 {{ title }}{{ cat.name }} 这类模板文本。

根因四:缓存策略不完整

nginx 如果没有合理的 gzip、静态资源缓存、视频范围请求和 HTML no-cache 策略,重复访问和大文件加载体验会明显变差。

最终策略:HTML 和备案域名仍走 yjxxdesign.com,视频等重资源优先走已有 yjxx.cloud;列表页图片用 WebP 缩略图,点击查看时再加载原图;Vue 页面挂载前隐藏模板,挂载后显示真实页面。

2. 诊断方法

2.1 先判断慢在哪里

  1. 打开浏览器 DevTools 的 Network 面板,勾选 Disable cache,分别用 Fast 4G / Slow 4G 模拟。
  2. 按 Size 或 Time 排序,看首屏最慢的是 HTML、JS、CSS、视频、图片还是第三方 CDN。
  3. 对比两个域名同一资源的响应头:curl -I https://domain/path/file.mp4
  4. 看视频是否支持 Range:响应应出现 Accept-Ranges: bytes 或请求返回 206 Partial Content
  5. 看 HTML 是否声明 charset:应为 text/html; charset=utf-8,否则中文页面可能出现乱码或短暂异常。

2.2 常用命令

# 查看某个页面是否已经包含优化后的代码
curl -L -s https://example.com/index.html | rg "lazy-video|media/mobile|thumbs"

# 查看视频是否支持范围请求
curl -I -H "Range: bytes=0-1023" https://example.com/media/video.mp4

# 找出本地大文件
find . -type f \( -iname '*.mp4' -o -iname '*.jpg' -o -iname '*.png' \) -print0 \
  | xargs -0 ls -lh | sort -k5 -hr | sed -n '1,80p'

# 搜索页面里的视频、图片引用
rg -n "<video|<source|\.mp4|\.jpg|\.png|assets/images|media/" *.html en/*.html

2.3 判断优先级

现象优先检查常见修复
首屏黑屏、视频卡住首屏 video 的体积、preload、CDN、poster压缩视频、换 CDN、设置 poster、仅保留一个 source
页面滚动前就很慢首屏下方资源是否提前加载IntersectionObserver 懒加载图片/视频
列表页慢卡片图是否加载原图WebP 缩略图 + lightbox 原图
闪过 {{ }}Vue 是否挂载前显示模板v-cloak + pending/ready class
第二次访问仍慢Cache-Control、gzip、静态资源缓存nginx 静态缓存和 gzip

3. 视频优化经验

3.1 视频优化的基本原则

  • 首屏只保留一个必要视频源。多个 <source> 不是“容灾”,浏览器仍会做类型判断和请求准备,可能增加不确定性。
  • 非首屏视频不要直接写 <source>data-video-src 保存地址,滚动到附近再创建 source
  • 必须设置 poster。视频加载前至少显示静态图,避免用户看到黑框。
  • 移动端和 PC 端可以共用压缩版。如果视觉效果能接受,优先复用已验证的压缩视频。
  • 重资源可走另一个 CDN 域名。本次把 PC 和移动端视频指向 https://yjxx.cloud/media/mobile/...,避免腾讯云源站直接扛大视频。

3.2 本次采用的策略

页面优化前优化后
移动端首页本地大视频,第二视频直接加载Cloudflare 压缩视频,第二视频滚动到附近加载
PC 首页本地 12MB-14MB 视频,硬件区视频直接加载首屏 Cloudflare 压缩视频,硬件区视频懒加载
视频黑框加载失败时只有黑色播放器保留 poster,避免视觉空白
注意:视频不要只看文件大小,还要看码率、分辨率、编码、是否支持 Range、CDN 节点响应和首帧时间。一个 8MB 的视频如果首帧慢,体验仍然不好。

4. 图片优化经验

4.1 列表页不要直接加载原图

hardware 页面原始 JPG 很多在 1MB-2MB,PC 卡片只是方形预览,没有必要加载原图。本次生成了 assets/images/hardware/thumbs/*.webp,多数只有 8KB-30KB,列表页速度提升明显。

4.2 推荐模式:缩略图 + 原图 lightbox

  • 卡片图使用 thumbs/name.webp
  • 点击放大仍使用原始 assets/images/hardware/name.jpg
  • 如果 WebP 缩略图加载失败,自动 fallback 到原图,保证不破图。
  • 移动端、PC 端都可以用同一批缩略图。

4.3 图片格式建议

用途推荐格式建议尺寸说明
卡片缩略图WebP480-800px 宽列表页速度优先
详情大图JPG/WebP1200-2000px 宽保留细节,按需加载
LogoSVG 优先矢量清晰、体积小
分享图JPG1200x630OG/Twitter 兼容性更好
二维码PNG/WebP300-600px避免压缩过度导致识别困难

5. Vue 模板闪烁和乱码处理

独立 HTML 页面使用 Vue 模板时,HTML 会先被浏览器解析,随后 Vue 才挂载。如果网络慢或 JS CDN 慢,用户可能看到 {{ currentCategory }}{{ prod.title }} 等模板内容。

5.1 推荐处理方式

  1. <html> 初始带 page-pending class。
  2. #app 上保留 v-cloak
  3. CSS 中挂载前隐藏 #app,显示一个轻量加载提示。
  4. Vue mount 后移除 pending,添加 ready。
本次对 modules.htmlen/modules.htmlhardware.htmlen/hardware.html 使用了这套方法,解决初始加载和点击 tab 时的模板外露问题。

6. nginx 与静态资源缓存

6.1 免费可做的 nginx 优化

  • 开启 gzip,压缩 HTML、CSS、JS、SVG、JSON。
  • JS/CSS 设置较长缓存,例如 7 天。
  • 图片、字体、视频设置更长缓存,例如 30 天。
  • HTML 设置 no-cache,避免发布后用户一直看到旧页面。
  • 确认服务器返回 charset utf-8
  • 保留视频 Range 请求能力,避免视频必须完整下载后才能播放。

6.2 HTML 和静态资源缓存要分开

不要给 HTML 设置超长强缓存。HTML 经常变,缓存太久会导致发布后用户访问旧版本。CSS/JS/图片/视频可以长缓存,最好通过文件名变更或内容 hash 控制更新。

7. CDN 与备案边界

7.1 不一定要迁移整个域名

如果主域名涉及备案、国内访问、现有 nginx 配置,不必一开始就迁移整个站点到 Cloudflare。可以先把大视频、大图片、下载包等重资源放到已有 Cloudflare 域名上,再由主站 HTML 引用。

7.2 本次经验

  • yjxxdesign.com 继续作为主站和备案域名。
  • yjxx.cloud 已经有 Cloudflare CDN,可作为视频等重资源域名。
  • HTML、业务页面、备案信息不迁移,降低合规和 DNS 风险。
  • 重资源跨域加载视频时要确认响应头和浏览器兼容性。普通 <video> 引用通常可以正常播放。
合规提醒:如果把国内备案主域名直接 CNAME 到境外 CDN 或迁出国内接入商,可能影响备案接入关系。只把重资源引用到另一个已存在域名,通常风险更低,但仍应按实际 ICP、接入商要求复核。

8. 验证与发布流程

8.1 本地验证

  • 启动本地静态服务,例如 python3 -m http.server 8080
  • 使用 curl -I 确认页面返回 200。
  • 使用浏览器确认页面无控制台错误。
  • 检查视频初始 source 是否符合预期。
  • 滚动到懒加载区域,确认 source 被注入。
  • 检查缩略图是否实际使用 thumbs/*.webp
  • 点击 lightbox,确认仍打开原图。

8.2 线上验证

# 检查线上 HTML 是否包含优化代码
curl -L -s https://example.com/index.html | rg "media/mobile|lazy-video|rootMargin"
curl -L -s https://example.com/hardware.html | rg "getCurrentThumbImg|thumbs"

# 检查关键资源响应
curl -I https://example.com/assets/images/hardware/thumbs/01-demo.webp
curl -I -H "Range: bytes=0-1023" https://cdn.example.com/media/demo.mp4

8.3 发布节奏

  1. 先小范围改动,例如只改移动首页或 PC 首页,不一次性改全站。
  2. 本地预览确认视觉无明显变化。
  3. 发布到线上后马上 curl 抽查关键代码。
  4. 确认线上 OK 后再提交 git,避免把失败部署沉淀到版本库。
  5. git 提交时排除 .wrangler.DS_Store__pycache__、压缩过程目录等临时文件。

9. 可复用代码片段

9.1 视频懒加载

<video
  class="lazy-video"
  autoplay
  loop
  muted
  playsinline
  preload="none"
  poster="./media/video-poster.jpg"
  data-video-src="https://cdn.example.com/media/video-compressed.mp4">
</video>

<script>
function loadLazyVideo(video){
  if(!video || video.dataset.loaded === 'true') return;
  var src = video.dataset.videoSrc;
  if(!src) return;

  var source = document.createElement('source');
  source.src = src;
  source.type = 'video/mp4';
  video.appendChild(source);
  video.dataset.loaded = 'true';
  video.load();

  try {
    var p = video.play();
    if(p && p.catch) p.catch(function(){});
  } catch(e) {}
}

var lazyVideos = document.querySelectorAll('video.lazy-video[data-video-src]');
if('IntersectionObserver' in window){
  var videoObserver = new IntersectionObserver(function(entries){
    entries.forEach(function(entry){
      if(entry.isIntersecting){
        loadLazyVideo(entry.target);
        videoObserver.unobserve(entry.target);
      }
    });
  }, { rootMargin: '150px 0px', threshold: 0.01 });

  lazyVideos.forEach(function(video){ videoObserver.observe(video); });
} else {
  lazyVideos.forEach(loadLazyVideo);
}
</script>

9.2 Vue 挂载前隐藏模板

<html lang="zh-CN" class="page-pending">
...
<style>
[v-cloak] { display: none; }
.page-pending body::before {
  content: "正在加载...";
  position: fixed;
  inset: 0;
  z-index: 9001;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #0b1121;
  color: #94a3b8;
}
.page-pending #app { visibility: hidden; }
.page-ready body::before { display: none; }
</style>
...
<div id="app" v-cloak>...</div>
<script>
const app = Vue.createApp({
  // data, computed, methods
});

app.mount('#app');
document.documentElement.classList.remove('page-pending');
document.documentElement.classList.add('page-ready');
</script>

9.3 缩略图 + 原图 fallback

// Vue methods 示例
getImages(item) {
  if (item.images && item.images.length) return item.images;
  return item.image ? [item.image] : [];
},
getCurrentImg(item) {
  const imgs = this.getImages(item);
  return imgs[this.imgIdx[item.id] || 0] || null;
},
getThumbImg(src) {
  if (!src) return null;
  return src
    .replace('/assets/images/products/', '/assets/images/products/thumbs/')
    .replace(/\.(jpe?g|png)$/i, '.webp');
},
getCurrentThumbImg(item) {
  const src = this.getCurrentImg(item);
  if (!src) return null;
  return this.thumbErrors[src] ? src : this.getThumbImg(src);
},
onThumbError(src) {
  if (src) this.thumbErrors[src] = true;
}

9.4 nginx 静态资源优化模板

charset utf-8;

gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml;

location ~* \.(?:css|js)$ {
  expires 7d;
  add_header Cache-Control "public, max-age=604800";
}

location ~* \.(?:png|jpg|jpeg|gif|webp|svg|ico|woff2?|ttf|mp4)$ {
  expires 30d;
  add_header Cache-Control "public, max-age=2592000";
}

location ~* \.html$ {
  add_header Cache-Control "no-cache";
}

10. 迁移到其他网站的 Checklist

10.1 第一轮:只读诊断

  • 列出页面所有视频、图片、第三方脚本。
  • 按文件大小排序,找出前 20 个重资源。
  • 确认首屏真正需要哪些资源。
  • 确认非首屏视频、图片是否可以懒加载。
  • 确认是否有 Vue/React/模板渲染闪烁。
  • 确认服务器是否有 gzip 和缓存头。

10.2 第二轮:低风险优化

  • 为视频增加 poster。
  • 非首屏视频改为懒加载。
  • 列表页图片改为缩略图。
  • 模板页面增加 pending/ready 状态。
  • nginx 增加 gzip、JS/CSS/图片/视频缓存。

10.3 第三轮:资源体系优化

  • 建立 media/mobile/media/desktop/assets/images/.../thumbs/ 目录。
  • 将视频压缩到适合网页播放的码率。
  • 将大图生成 WebP/AVIF 缩略图。
  • 把重资源迁到 CDN 或对象存储。
  • 为 CDN 资源设置长期缓存。

10.4 上线前最后确认

  • 视觉效果没有明显降级。
  • 首屏不出现黑框、白屏、模板花括号。
  • 移动端和 PC 端都能正常播放视频。
  • 图片缩略图加载失败时能 fallback。
  • 发布后用线上 URL 抽查,而不是只看本地。
  • git 提交只包含业务文件,排除缓存和临时文件。
实用原则:先处理“首屏视频、列表图、模板闪烁、静态缓存”四件事,通常能用最低成本解决 80% 的页面打开慢问题。

11. 术语释义

这一节把文档里出现的前端、服务器、CDN 名词翻译成更日常的说法。以后给非技术同事或客户解释页面为什么慢、为什么这样优化时,也可以直接参考。

名词 通俗解释 本次优化里的作用
HTML 网页的骨架,决定页面上有哪些标题、按钮、图片、视频、表格等内容。 我们修改的多数页面都是 HTML 文件,比如首页、hardware 页面、modules 页面。
CSS 网页的装修和排版,决定颜色、字号、布局、间距、按钮样式等。 加载遮罩、缩略图容器、导航栏样式都靠 CSS 控制。
JavaScript / JS 网页里的交互逻辑,让页面能搜索、切换 tab、打开弹窗、懒加载视频。 Vue 挂载、视频懒加载、lightbox 放大图都需要 JS。
首屏 用户刚打开页面、不滚动时第一眼看到的区域。 首屏越快出现,用户越觉得网站快。首屏视频、首屏图片、首屏 JS 是优化重点。
静态资源 不会每次动态生成的文件,例如图片、视频、CSS、JS、字体。 这些文件可以缓存,缓存后下次打开页面会更快。
源站 网站文件真正存放的服务器,可以理解成“总仓库”。 yjxxdesign.com 的源站在腾讯云服务器上,HTML 和资源原始文件从这里同步出去。
CDN 分布在各地的加速节点。用户访问资源时,不一定回源站拿,而是从更近或更快的节点拿。 视频放到 yjxx.cloud 走 Cloudflare CDN,减少腾讯云源站的大文件压力。
Cloudflare 一个常见的全球 CDN 和安全服务平台,能帮网站缓存资源、抵御攻击、提升部分地区访问速度。 yjxx.cloud 使用 Cloudflare,重视频资源复用这个域名加载。
腾讯云 CDN / 腾讯云服务器 腾讯云提供的国内云服务器和加速服务。服务器是放网站文件的机器,CDN 是加速网络。 yjxxdesign.com 主要部署在腾讯云服务器,适合承载备案主站和 HTML。
nginx 服务器上的“门卫和分发员”。浏览器请求页面时,nginx 决定返回哪个文件、加什么响应头、是否压缩。 我们在 nginx 上补了 gzip、缓存、charset 等配置,提升静态文件访问体验。
gzip 一种压缩方式。服务器把文本类文件压小再发给浏览器,浏览器收到后自动解压。 HTML、CSS、JS、SVG 这类文本文件开启 gzip 后,传输体积会更小。
Cache-Control 服务器告诉浏览器“这个文件能缓存多久”的规则。 JS/CSS/图片/视频适合缓存更久,HTML 适合 no-cache,避免发布后还看到旧页面。
no-cache 不是完全不缓存,而是每次使用前先问服务器“有没有新版本”。 HTML 用 no-cache,用户刷新后更容易拿到最新页面。
Range 请求 浏览器可以只请求文件的一小段,而不是一次下载完整文件。 视频支持 Range 后,可以边下边播、拖动进度条也更顺。
206 Partial Content 服务器返回“这是你要的文件其中一段”的状态码。 看到 206 通常说明 Range 请求生效,视频播放体验会更好。
poster 视频加载前显示的一张封面图。 视频还没加载好时先显示 poster,避免出现黑框。
preload 告诉浏览器视频或音频要不要提前加载。 首屏视频用 metadata,非首屏视频用 none,减少一打开页面就抢带宽。
懒加载 / Lazy Loading 东西快要用到时才加载,不是一打开页面就全加载。 第二个视频滚动到硬件区附近才加载,列表下方图片也可以懒加载。
IntersectionObserver 浏览器提供的“观察元素是否快进入屏幕”的能力。 用它判断第二个视频是否接近视口,接近了才插入视频 source。
WebP 一种图片格式,通常比 JPG/PNG 更小,同时保持不错的清晰度。 hardware 卡片图改用 WebP 缩略图,从 1MB 级别降到几十 KB。
缩略图 原图的轻量预览版,尺寸更小、体积更小。 列表页只需要看大概样子,所以用缩略图;点击查看细节时再加载原图。
lightbox 点击图片后弹出的放大查看层,通常背景变暗,图片居中显示。 hardware 页面卡片用缩略图,但 lightbox 仍打开原始 JPG,保证细节清楚。
Vue 一种前端框架,适合做搜索、列表筛选、tab 切换等动态页面。 hardware、modules 这些目录页用 Vue 管理产品数据和分类切换。
v-cloak Vue 提供的一个标记,用来在 Vue 接管页面前隐藏未处理的模板。 避免用户看到 {{ prod.title }} 这类模板文本。
pending / ready class 页面加载状态标记。pending 表示“还没准备好”,ready 表示“已经渲染好”。 Vue 挂载前隐藏 #app 并显示加载提示,挂载后切到 ready 展示真实页面。
Fallback 备用方案。主方案失败时,自动退回到另一个可用方案。 WebP 缩略图加载失败时,fallback 到原始 JPG,保证页面不破图。
跨域资源 页面从一个域名加载另一个域名的资源,例如 yjxxdesign.com 页面加载 yjxx.cloud 视频。 本次视频跨域加载是为了复用 Cloudflare CDN 上的重资源。
备案 / ICP 接入 国内网站域名通常要在工信部备案,并和实际接入服务商保持关系。 主域名不迁移,只把重资源放到已有 Cloudflare 域名,能降低备案接入变化风险。
DevTools / Network 浏览器开发者工具里的网络面板,可以看到每个文件加载耗时和体积。 判断页面慢在哪里时,Network 面板比主观感受更可靠。
curl 命令行里的网页请求工具,可以快速查看页面内容和服务器响应头。 发布后用 curl 抽查线上页面是否已经包含优化代码。
git 代码版本管理工具,记录每次修改,方便回滚和多人协作。 优化上线后,把确认过的改动提交到 git,避免本地和线上版本丢失。
一句话理解:页面打开快不快,通常取决于“首屏要下载多少东西、这些东西从哪里下载、浏览器能不能缓存、没加载完时页面是否有友好占位”。这些术语本质上都是围绕这四件事服务。