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 策略,重复访问和大文件加载体验会明显变差。
yjxxdesign.com,视频等重资源优先走已有 yjxx.cloud;列表页图片用 WebP 缩略图,点击查看时再加载原图;Vue 页面挂载前隐藏模板,挂载后显示真实页面。
2. 诊断方法
2.1 先判断慢在哪里
- 打开浏览器 DevTools 的 Network 面板,勾选 Disable cache,分别用 Fast 4G / Slow 4G 模拟。
- 按 Size 或 Time 排序,看首屏最慢的是 HTML、JS、CSS、视频、图片还是第三方 CDN。
- 对比两个域名同一资源的响应头:
curl -I https://domain/path/file.mp4。 - 看视频是否支持 Range:响应应出现
Accept-Ranges: bytes或请求返回206 Partial Content。 - 看 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,避免视觉空白 |
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 图片格式建议
| 用途 | 推荐格式 | 建议尺寸 | 说明 |
|---|---|---|---|
| 卡片缩略图 | WebP | 480-800px 宽 | 列表页速度优先 |
| 详情大图 | JPG/WebP | 1200-2000px 宽 | 保留细节,按需加载 |
| Logo | SVG 优先 | 矢量 | 清晰、体积小 |
| 分享图 | JPG | 1200x630 | OG/Twitter 兼容性更好 |
| 二维码 | PNG/WebP | 300-600px | 避免压缩过度导致识别困难 |
5. Vue 模板闪烁和乱码处理
独立 HTML 页面使用 Vue 模板时,HTML 会先被浏览器解析,随后 Vue 才挂载。如果网络慢或 JS CDN 慢,用户可能看到 {{ currentCategory }}、{{ prod.title }} 等模板内容。
5.1 推荐处理方式
<html>初始带page-pendingclass。#app上保留v-cloak。- CSS 中挂载前隐藏
#app,显示一个轻量加载提示。 - Vue
mount后移除 pending,添加 ready。
modules.html、en/modules.html、hardware.html、en/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 和静态资源缓存要分开
7. CDN 与备案边界
7.1 不一定要迁移整个域名
如果主域名涉及备案、国内访问、现有 nginx 配置,不必一开始就迁移整个站点到 Cloudflare。可以先把大视频、大图片、下载包等重资源放到已有 Cloudflare 域名上,再由主站 HTML 引用。
7.2 本次经验
yjxxdesign.com继续作为主站和备案域名。yjxx.cloud已经有 Cloudflare CDN,可作为视频等重资源域名。- HTML、业务页面、备案信息不迁移,降低合规和 DNS 风险。
- 重资源跨域加载视频时要确认响应头和浏览器兼容性。普通
<video>引用通常可以正常播放。
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 发布节奏
- 先小范围改动,例如只改移动首页或 PC 首页,不一次性改全站。
- 本地预览确认视觉无明显变化。
- 发布到线上后马上 curl 抽查关键代码。
- 确认线上 OK 后再提交 git,避免把失败部署沉淀到版本库。
- 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 提交只包含业务文件,排除缓存和临时文件。
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,避免本地和线上版本丢失。 |