1.预加载图片
通过JavaScript动态创建Image对象:通过new Image()预加载图片数组,适用于需要精确控制加载时机的场景
2.懒加载图片
懒加载通过延迟非可视区域图片的请求来优化首屏性能。主流实现方案有
- 原生属性:HTML5的
loading="lazy"属性,简单但存在兼容性问题- Intersection Observer API:现代浏览器推荐方案,通过观察元素与视窗交叉状态触发加载,性能优于传统滚动事件。
- 传统滚动监听:计算元素
offsetTop与视窗位置关系,需配合节流函数优化性能。
3.压缩图片
-
Commpressor.js (推荐)
Compressor.js通过Canvas原生实现像素级压缩,在保持90%视觉质量的同时,实现平均65-85%的压缩率。兼容下现代浏览器(包括移动端),支持主流图片格式(JPEG/PNG/WEBP)。
参考:字节跳动图像压缩方案
导入依赖
npm install compressorjs
或者
yarn add compressorjs
或者直接引入cdn的压缩js。
<script src="https://cdn.jsdelivr.net/npm/compressorjs@1.2.1/dist/compressor.min.js"></script>
基础使用
// 获取文件对象(例如通过input[type=file])
const file = document.querySelector('input[type=file]').files[0];
// 初始化压缩器
new Compressor(file, {
quality: 0.8, // 压缩质量(0-1)
maxWidth: 1920, // 最大宽度
maxHeight: 1080, // 最大高度
success(result) {
// 压缩成功回调
const formData = new FormData();
formData.append('file', result, result.name);
// 上传到服务器示例
fetch('/api/upload', {
method: 'POST',
body: formData,
});
},
error(err) {
console.error('压缩失败:', err);
},
});
进阶配值
new Compressor(file, {
// 压缩质量配置
quality: 0.6,
// 尺寸限制
width: 800, // 固定宽度
height: 600, // 固定高度
resize: 'cover', // 缩放模式(cover/contain等)
// 格式转换
convertSize: 102400, // 超过100KB自动转成JPEG
mimeType: 'auto', // 自动检测最佳格式
// 高级处理
strict: true, // 严格模式(精确尺寸)
orient: true, // 保持EXIF方向信息
checkOrientation: true, // 检查EXIF方向
// 钩子函数
beforeDraw(ctx, canvas) {
// 压缩前可对Canvas进行额外处理
ctx.filter = 'grayscale(100%)';
},
drew(ctx, canvas) {
// 压缩后可进行后处理
const watermark = document.createElement('img');
watermark.onload = () => {
ctx.drawImage(watermark, 10, 10, 100, 50);
};
watermark.src = '/watermark.png';
},
});
完整代码
<!DOCTYPE html>
<input type="file" accept="image/*" id="uploader">
<div id="preview"></div>
<script src="https://cdn.jsdelivr.net/npm/compressorjs@1.2.1/dist/compressor.min.js"></script>
<script>
document.getElementById('uploader').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
new Compressor(file, {
quality: 0.7,
maxWidth: 1024,
convertSize: 500000, // 超过500KB转成WEBP
success(result) {
// 显示压缩前后对比
const preview = document.getElementById('preview');
// 原始文件预览
const origin = new Image();
origin.src = URL.createObjectURL(file);
origin.style = 'width: 300px; border: 2px solid red;';
// 压缩后预览
const compressed = new Image();
compressed.src = URL.createObjectURL(result);
compressed.style = 'width: 300px; border: 2px solid green;';
// 显示文件信息
const info = document.createElement('div');
info.innerHTML = `
原始大小:${(file.size/1024).toFixed(2)}KB
压缩后:${(result.size/1024).toFixed(2)}KB
压缩率:${(100 - (result.size/file.size*100)).toFixed(1)}%
`;
preview.append(origin, compressed, info);
},
error(err) {
alert(`压缩失败: ${err.message}`);
}
});
});
</script>
注意事项
-
格式转换限制:Safari 14以下不支持WEBP格式
-
质量设置建议:
-
JPEG推荐0.6-0.8
-
PNG推荐保持1.0(无损)
-
-
性能优化:大文件(>5MB)建议先分片读取
-
兼容性处理:对于旧版浏览器需要polyfill:
// 添加Promise polyfill
if (typeof Promise === 'undefined') {
script.async = false;
document.write('<script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.8/dist/es6-promise.auto.min.js"><\/script>');
}
<script src="https://cdn.jsdelivr.net/npm/compressorjs@1.2.1/dist/compressor.min.js"></script>
<input type="file" id="uploader" accept="image/*" multiple />
<script>
document.getElementById('uploader').addEventListener('change', e => {
Array.from(e.target.files).forEach(file => {
new Compressor(file, {
quality: 0.7,
maxWidth: 1024,
maxHeight: 1024,
convertSize: 500000, // >500KB 转 WebP
success(result) {
console.log(`${(file.size/1024).toFixed(1)}KB → ${(result.size/1024).toFixed(1)}KB`);
// 可直接 `fetch('/upload', { method:'POST', body: result })`
},
error(err) {
alert(err.message);
}
});
});
});
</script>
-
原生 Canvas 压缩(零依赖)

<input type="file" id="file" accept="image/*" />
<button onclick="handle()">压缩并预览</button>
<canvas id="canvas" style="border:1px solid #000;max-width:100%;"></canvas>
<script>
async function handle() {
const file = document.getElementById('file').files[0];
if (!file) return;
const blob = await compressImage(file, { maxWidth: 800, quality: 0.7 });
const url = URL.createObjectURL(blob);
document.getElementById('canvas').src = url;
console.log(`压缩前 ${(file.size/1024).toFixed(1)}KB → 压缩后 ${(blob.size/1024).toFixed(1)}KB`);
}
</script>
/**
* 将 File 压缩成 Blob
* @param {File} file 原文件
* @param {Object} opts 配置
* maxWidth: 最大宽度(px)
* maxHeight:最大高度(px)
* quality: JPEG/WebP 质量 0-1
* @returns {Promise<Blob>} 压缩后 Blob
*/
async function compressImage(file, opts = {}) {
const { maxWidth = 800, maxHeight = 600, quality = 0.8 } = opts;
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 等比缩放
let { width, height } = img;
if (width > maxWidth || height > maxHeight) {
const scale = Math.min(maxWidth / width, maxHeight / height);
width *= scale;
height *= scale;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(resolve, file.type === 'image/png' ? 'image/png' : 'image/jpeg', quality);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
1288

被折叠的 条评论
为什么被折叠?



