Webpack Loader开发实战与常见问题解决思路
先看效果,再看代码
前几天做项目的时候遇到一个需求:用户上传的文件需要在前端进行预处理,比如图片压缩、格式校验啥的。一开始我想直接用现成的库,后来发现这些库要么太重,要么功能不够灵活。于是就自己动手写了一个Loader模块,亲测有效。
核心思路其实很简单:通过FileReader读取文件内容,然后对数据流进行各种操作。下面这段代码是图片压缩的核心逻辑:
function compressImage(file, quality = 0.7) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
const img = new Image();
img.src = event.target.result;
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
canvas.toBlob(
(blob) => resolve(blob),
'image/jpeg',
quality
);
};
img.onerror = reject;
};
reader.onerror = reject;
});
}
调用的时候只需要传入文件对象和压缩质量,就能得到一个新的Blob对象,可以直接上传到服务器或者进一步处理。
这个场景最好用
上面的代码看着简单,但实际项目中会有很多变种需求。比如有时候不只是图片压缩,还需要对文件类型做校验,或者限制文件大小。我一般会在Loader里加一层拦截器:
class FileLoader {
constructor() {
this.middleware = [];
}
use(fn) {
this.middleware.push(fn);
}
async load(file) {
let result = file;
for (const middleware of this.middleware) {
result = await middleware(result);
if (!result) throw new Error("File processing failed");
}
return result;
}
}
// 使用示例
const loader = new FileLoader();
loader.use(async (file) => {
if (file.size > 2 * 1024 * 1024) {
console.warn("File too large, skipping...");
return null;
}
return file;
});
loader.use(async (file) => compressImage(file, 0.6));
这个设计的好处是特别灵活。比如你可以在use方法里插入各种规则:文件类型校验、尺寸限制、甚至调用第三方服务。而且每个中间件都是独立的函数,复用起来非常方便。
踩坑提醒:这三点一定注意
看起来挺顺滑的对吧?但实际上我踩了好几个坑,这里给大家提个醒:
- FileReader的异步问题:刚开始我没注意,把FileReader的结果直接当同步值用了,结果调试了半天才发现它是异步的。解决办法就是老老实实用Promise封装一下。
- Canvas的内存泄漏:如果你频繁处理大图片,Canvas可能会占用大量内存,导致页面卡顿甚至崩溃。建议每次用完手动清空画布:
ctx.clearRect(0, 0, canvas.width, canvas.height)。 - Blob转File的问题:有些后端接口要求必须是File对象而不是Blob,这时候可以用下面这个小技巧:
function blobToFile(blob, fileName) {
return new File([blob], fileName, { type: blob.type });
}
高级玩法:结合Web Worker
如果文件特别大,或者处理逻辑特别复杂,直接在主线程跑可能会阻塞UI。这时候可以考虑用Web Worker来处理。下面是我在项目里用的一个例子:
// worker.js
self.addEventListener('message', async (event) => {
const { file, quality } = event.data;
const compressedBlob = await compressImage(file, quality);
self.postMessage(compressedBlob);
});
// 主线程
const worker = new Worker('./worker.js');
worker.postMessage({ file, quality: 0.6 });
worker.onmessage = (event) => {
const processedFile = event.data;
console.log("Processed file:", processedFile);
};
虽然多了一层复杂度,但性能提升很明显,尤其是移动端。不过要注意,Worker里的代码不能访问DOM,所以像Canvas这种东西得放在线程外部处理。
结尾碎碎念
以上就是我最近折腾Loader的一些经验总结。说实话,开发过程中确实有点折磨人,尤其是处理那些异步逻辑和边界情况的时候。不过最终的效果还是让我挺满意的,代码也比之前清爽了不少。
这个技术的拓展用法还有很多,比如支持视频预览、PDF解析啥的,后续我会继续分享这类博客。如果你有更好的实现方式,或者发现了什么隐藏的坑,欢迎评论区交流!

暂无评论