提高网站加载速度,避免用户焦头烂额
更新: 2025/10/15 字数: 0 字 时长: 0 分钟
一、服务器上的配置
CDN缓存
提前从原服务器获取到网站资源,转发缓存到全国各地的CDN节点,用户在进行网站访问时,就可以就近选择最近的节点获取资源,避免远距离传输的效率低下问题。

优点:延迟更低、允许更多人访问
缺点:在服务器上设置好缓存规则,否则会被恶意刷取流量(按流量计费,贵)
浏览器缓存
更直接的方法,在用户第一次访问后缓存到本地,下一次访问时直接本地加载
缓存的内容可以根据类型不同,设置不一样的时间,例如静态资源,可以极长时间缓存(1年以上);动态资源,html页面(可以设置几分钟到几小时);敏感资源等,不进行缓存,避免泄露。
升级HTTP2
HTTP1处理逻辑是串行处理,虽然可以建立多个连接,但是连接内的请求要按顺序处理,容易产生队头阻塞问题,而HTTP2可以多路复用,真正意义上在单个连接上处理多个请求,实现并行处理
1.可在宝塔网站中的配置文件处,添加一行代码即可
2.如果使用CDN,可以在CDN中自行勾选打卡这个配置

宝塔中的配置

#关键:在443端口后添加 http2
listen 443 ssl http2;当然,HTTP3也可以使用,理论上建立连接速度更快,完全解决队头阻塞问题,不过兼容性和稳定性有待验证,暂时不考虑
二、网站本体修改
静态资源压缩
对于网站的静态资源如图片视频,大家也都清楚,体积越大,传输速度越慢,所以我们可以考虑将这些内容进行压缩。
jpg、png等图片可以进行webp格式压缩,能减少20%-90%左右的体积。
网站中如果有用户上传图片的功能,要在后端进行压缩图片代码的编写,压缩完再上传服务器,否则原图较大会消耗大量流量
python批处理的代码如下,使用示例:
#80是压缩质量 -c是开始前清除输出目录
python compress_images.py -i my_photos -o optimized_webp -q 85 -c代码
import os
import time
from datetime import timedelta
from PIL import Image
import argparse
import shutil
def compress_images(input_folder, output_folder, quality=75):
"""
将输入文件夹中的图片压缩为WebP格式并保存到输出文件夹
参数:
input_folder: 输入图片文件夹路径
output_folder: 输出文件夹路径
quality: WebP压缩质量 (0-100)
"""
# 支持的图片格式
valid_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.gif')
# 确保输出文件夹存在
os.makedirs(output_folder, exist_ok=True)
# 初始化统计变量
total_files = 0
processed_files = 0
skipped_files = 0
original_total_size = 0
compressed_total_size = 0
start_time = time.time()
print(f"📂 源文件夹: {os.path.abspath(input_folder)}")
print(f"💾 目标文件夹: {os.path.abspath(output_folder)}")
print(f"⚙️ 压缩质量: {quality}\n")
print("=" * 60)
# 遍历输入文件夹
for filename in os.listdir(input_folder):
input_path = os.path.join(input_folder, filename)
# 检查文件扩展名
if filename.lower().endswith(valid_extensions):
total_files += 1
# 获取原始文件大小
original_size = os.path.getsize(input_path)
original_total_size += original_size
# 创建输出路径
output_filename = os.path.splitext(filename)[0] + '.webp'
output_path = os.path.join(output_folder, output_filename)
try:
# 打开并转换图片
with Image.open(input_path) as img:
if img.mode in ('RGBA', 'LA', 'P'):
img = img.convert('RGBA')
else:
img = img.convert('RGB')
# 保存为WebP格式
img.save(output_path, 'WEBP', quality=quality, method=6)
# 获取压缩后文件大小
compressed_size = os.path.getsize(output_path)
compressed_total_size += compressed_size
processed_files += 1
# 计算单个文件压缩率
ratio = (original_size - compressed_size) / original_size * 100
# 打印单个文件结果
print(f"[{processed_files}/{total_files}] ✓ {filename} → {output_filename}")
print(f" 原始: {format_size(original_size)} → 压缩后: {format_size(compressed_size)}")
print(f" 节省: {format_size(original_size - compressed_size)} ({ratio:.1f}%)\n")
except Exception as e:
skipped_files += 1
print(f"✗ 跳过 {filename} - 错误: {str(e)}\n")
# 计算总处理时间
processing_time = time.time() - start_time
# 打印最终统计信息
print("=" * 60)
print("📊 转换统计:")
print(f" 扫描文件总数: {total_files}")
print(f" 成功转换: {processed_files}")
print(f" 跳过文件: {skipped_files}")
print(f" 处理时间: {timedelta(seconds=round(processing_time))}")
# 只有处理了文件才显示压缩统计
if processed_files > 0:
# 计算总体压缩率
overall_ratio = (original_total_size - compressed_total_size) / original_total_size * 100
print("\n📦 体积统计:")
print(f" 原始总大小: {format_size(original_total_size)}")
print(f" 压缩后总大小: {format_size(compressed_total_size)}")
print(f" 总节省空间: {format_size(original_total_size - compressed_total_size)}")
print(f" 总体压缩率: {overall_ratio:.1f}%")
# 计算平均压缩率
avg_original = original_total_size / processed_files
avg_compressed = compressed_total_size / processed_files
avg_ratio = (avg_original - avg_compressed) / avg_original * 100
print(f"\n📈 平均每张图片:")
print(f" 原始: {format_size(avg_original)} → 压缩后: {format_size(avg_compressed)}")
print(f" 平均节省: {format_size(avg_original - avg_compressed)} ({avg_ratio:.1f}%)")
# 输出文件夹信息
print(f"\n✅ 处理完成! 输出目录: {os.path.abspath(output_folder)}")
def format_size(size_bytes):
"""将字节大小转换为易读的格式"""
if size_bytes < 1024:
return f"{size_bytes} bytes"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.2f} KB"
elif size_bytes < 1024 * 1024 * 1024:
return f"{size_bytes / (1024 * 1024):.2f} MB"
else:
return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"
if __name__ == "__main__":
# 设置命令行参数
parser = argparse.ArgumentParser(
description='📸 批量图片压缩工具 (转换为WebP格式)',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument('-i', '--input', default='images',
help='输入文件夹路径 (默认: images)')
parser.add_argument('-o', '--output', default='compressed_webp',
help='输出文件夹路径 (默认: compressed_webp)')
parser.add_argument('-q', '--quality', type=int, default=75,
help='压缩质量 (0-100, 默认: 75)')
parser.add_argument('-c', '--clean', action='store_true',
help='清空输出目录(如果存在)')
args = parser.parse_args()
# 验证质量参数
if args.quality < 0 or args.quality > 100:
print("错误: 质量参数必须在0-100之间")
exit(1)
# 如果需要清空输出目录
if args.clean and os.path.exists(args.output):
shutil.rmtree(args.output)
print(f"已清空输出目录: {args.output}")
# 执行转换
compress_images(
input_folder=args.input,
output_folder=args.output,
quality=args.quality
)手动代码压缩
对于CSS、JS代码、注释代码等,可以进行压缩处理,方式:注释代码可以删除,变量名缩短等等,一般的前端打包工具可以进行压缩
function getCompressConfig() {
return {
minify: 'terser',//使用 Terser 进行JS压缩cssMinify:true,/启用CSS压编terserOptions:{
cssMinify:true,
terserOptions:{
compress:{
drop_console:true,//移除所有console.log语句
drop_debugger:true,//移除所有debugger语句
pure_funcs:['console.log'],// 额外指定要移除的纯函数
passes:2// 执行 2轮压缩优化
},
mangle:{
toplevel:true//混淆顶级作用域的变量名
},
format:{
comments:false// 移除所有代码注释
}
}
}
}自动gzip压缩
浏览器和服务器间有协议,浏览器在请求头告诉服务器开启了gzip压缩,服务器收到请求后即可开启压缩,将文件压缩后再上传,通过请求头告诉浏览器是压缩后的文件,然后浏览器自动解压

三、加载策略
懒加载
延迟加载用户看不到的内容,当用户查看到时才加载,这个可以显著提升首屏显示时间(适合滚动 的元素)
模块化按需加载
网站的css、js拆分成多个文件,访问到某个页面时,只加载对于页面的css和js,也能提升访问速度
分层加载
先让用户看到内容,点击访问时才加载高质量内容,例如预览显示缩略图,点击后才显示高清图
渐进式加载
和分层类似但更高级,比如图片先显示模糊的图片预览,然后逐渐清晰,这样子能保证元素不会错位等
预加载
通常用于文章等内容,当用户访问文章列表时,预加载某些文章的内容,点击详情时用户即可实现无感秒进的效果(但是要注意加载的数量,根据情况加载)
四、请求合并
浏览器可能对同一域名有请求限制,所以要调用多次后端接口请求数据时,可能产生排队和阻塞,可以修改后端代码,合并请求,也可以搭建node中间层来做
tips:图标文件可以整合成一个长宽规范的文件,利用css雪碧图特性,把所有图标合并成一张,在前端利用css的background position加载图片指定位置
2025/10/15
继述
在进行HyperUI网站的开发时,第一次使用了chrome预渲染的新特性:Speculation Rules API
预计用户会访问这些URL,可以提前预加载或预渲染它们
有两种形式,分为Prefetch(预取)和Prerender(预渲染),前者是只下载页面资源,包括HTML、图片、脚本等,但是不执行,后者真正加载并渲染整个界面,但在后台不可见,当用户点击后实现**“瞬间切换”**!
欸,有没有这么神奇我也不知道 ( ̄、 ̄)
1.声明speculation Rules JSON
代码示例如下,在index.html添加script,告诉浏览器哪些页面要预渲染
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/next-page.html"] }
]
}
</script>字段详解
| 字段 | 含义 |
|---|---|
source | 来源类型,可为 "list" 或 "document" |
"list" | 直接列出要预渲染的 URL |
"document" | 从文档中选取匹配的元素(如 <a>) |
urls | 要预渲染或预取的 URL 列表 |
selectors | 用于选择 DOM 元素的 CSS 选择器 |
实际应用中如下,通过source定位资源类型,然后根据eagerness的期望程度,“希望浏览器在后台预渲染/xxx.html的界面”
::: example
<script type="speculationrules">
{
"prerender": [
{
"source": "document",
"where": {
"href_matches": "/components/*"
},
"eagerness": "moderate"
},
{
"source": "document",
"where": {
"href_matches": "/create"
},
"eagerness": "moderate"
},
{
"source": "document",
"where": {
"href_matches": "/profile/*"
},
"eagerness": "moderate"
},
{
"source": "document",
"where": {
"selector_matches": "a[href^='/components/']"
},
"eagerness": "eager"
},
{
"source": "document",
"where": {
"selector_matches": ".card a, .side-link"
},
"eagerness": "moderate"
}
],
"prefetch": [
{
"source": "document",
"where": {
"href_matches": "/api/*"
},
"eagerness": "moderate"
}
]
}
</script>::: 这种就是最基础的用法,直接写好每条页面地址的路径去获取资源预渲染,如果是使用了某些前端框架,可以参考下面的规则
好的本期文章到此结束,不用往下看了 ( ̄_, ̄ )
2.动态生成规则(常用于单页面应用SPA)
适用:Vue/React/Next.js/VitePress等前端框架
const speculationScript = document.createElement('script');
speculationScript.type = 'speculationrules';
speculationScript.textContent = JSON.stringify({
prerender: [
{ source: 'list', urls: ['/profile', '/settings'] }
]
});
document.head.appendChild(speculationScript);3.特定元素自动触发
这种方法就时候想在某个小元素内进行触发,而且是用户使用量最高的链接,例如个人中心,首页等,我们就可以在<a>标签中加上rel="prerender" 或 rel="prefetch":
<a href="/next-page.html" rel="prerender">下一页</a>或者动态触发,这个有点高级
document.querySelectorAll('a').forEach(link => {
link.addEventListener('mouseenter', () => {
const script = document.createElement('script');
script.type = 'speculationrules';
script.textContent = JSON.stringify({
prerender: [{ source: 'list', urls: [link.href] }]
});
document.head.appendChild(script);
});
});- 鼠标悬停后,浏览器开始预渲染目标页面;
- 用户点击时,立即显示该页面;
- 若用户未点击,则浏览器稍后会自动销毁预渲染页面,避免浪费资源。
调试方法
在Chrome DevTools地址栏中输入:chrome://speculation-rules
即可查看当前页面所有预渲染状态
使用策略
💡 七、实际使用策略(推荐)
| 场景 | 策略 |
|---|---|
| 用户鼠标悬停菜单项 | 动态注入 prerender |
| 首页已知下一步访问路径 | 固定 list 规则 prerender |
| 内容加载量大但交互少 | 使用 prerender |
| 数据接口、图片等资源 | 使用 prefetch |
TIP
这个特性有同源特性,即智能渲染同源(域名、端口、协议相同)的页面
不支持登录状态切换,若预渲染页面依赖登录态变化,可能出错
如果浏览器资源紧张,会触发自动回收,终止prerender