背景
很多项目随着业务和经手人的变更往往会变得臃肿,我想通过 Review 的视角来看,一般我们如何通过一些简单的优化避免低质量代码的产生。
初始代码及问题
初始的代码实现
async function handleFileListChange() {
fileListRef.value.forEach(async (element) => {
try {
const previewUrl = await getPreviewUrl(element.file);
let tempCanvas = await imgToCanvas(previewUrl);
const canvas = addWatermark(tempCanvas, watermarkText.value);
const img = convasToImg(canvas);
if (!previews.value.some((item) => item.name === element.name)) {
previews.value.push({
name: element.name,
src: img.src
});
}
} catch (error) {
console.error('获取预览 URL 时出错:', error);
}
});
}
以上代码是我之前一个项目下面一个算是职场老人的代码,功能比较简单,把上传的图片加上水印后,本地处理完预览显示出来,方便后续的操作
这段代码存在两个比较重要的问题:
- 逐个文件处理,效率较低。使用
forEach循环逐个处理文件,不能充分利用并行处理的优势,尤其是在处理大量文件时,速度会比较慢。 - 使用
some方法进行重复判断,效率不高。每次判断一个文件是否已经处理过,都需要遍历整个previews数组,随着数组的增大,性能会逐渐下降。
优化方案
这类优化,其实用map和new Set来进行优化不难。
优化后的代码如下:
async function processFile(element) {
const previewUrl = await getPreviewUrl(element.file);
const tempCanvas = await imgToCanvas(previewUrl);
const canvas = addWatermark(tempCanvas, watermarkText.value);
const img = convasToImg(canvas);
return { name: element.name, src: img.src };
}
async function handleFileListChange() {
const processedPreviews = await Promise.all(fileListRef.value.map(processFile));
const previewNames = new Set(previews.value.map(item => item.name));
previews.value = previews.value.concat(processedPreviews.filter(item =>!previewNames.has(item.name)));
}
优化思路
-
使用
map和Promise.all并行处理文件:- 首先定义了一个
processFile函数,用于处理单个文件,将文件转换为带有水印的图片,并返回包含文件名和图片源地址的对象。 - 在
handleFileListChange函数中,使用map方法将文件列表转换为一个处理文件的 Promise 数组,然后通过Promise.all并行处理这些 Promise,可以提高处理速度。
- 首先定义了一个
-
使用
Set进行快速重复判断:- 创建一个
Set对象previewNames,通过将已有的previews数组中的文件名映射到Set中,实现快速的重复判断。 - 使用
filter方法过滤已经处理过的文件,只保留新的文件添加到previews数组中。
- 创建一个
我之前经常感觉刷算法之类的是不是真的只是为了应付面试,其实抛去目的本身,在业务线多写几年代码,关于数据处理的优化,其实往往就是依赖于我们平时的基础功和刷的哪些算法题