canvas 正在慢慢吃掉你的内存...
canvas 概述
canvas
是一个可以通过 JavaScript 脚本来绘制图形的 HTML 元素,通常我们可以通过 canvas 来绘制图表、制作构图或者简单的动画。
与 canvas 的偶遇
在最近开发工作中,我最经常使用到 canvas
的场景是拿到已知的图片 URL,利用 canvas.toDataURL()
方法,将其转换为 base64 的图片格式。
除此之外,canvas
提供了 toBlob()
方法来创建 Blob
对象,这意味着,我们可以利用 canvas
将图片 URL 转换为 Blob
对象。
由此可见,当需要通过图片 URL 来转换为其他图片形式,我们就得经常和 canvas
打交道。
canvas 内存限制
如果你对 canvas
并不了解,就很容易忽略 canvas
的内存问题。当我看到这篇文章 mp.weixin.qq.com/s/hFG1ypsEc…,我才知道,如果 canvas
创建后没有及时回收,是会占用内存的,占用过多甚至会超出 canvas
内存限制,导致图片加载失败。
举个例子,如下面的代码,我们通过数组收集 canvas
对象,防止它被垃圾回收,并且声明一个创建 canvas
的方法,每个 canvas
宽高都为 512px,一个 canvas
所占用的内存即为 1MB:
html:
<div><span>内存单位: MB</span><input type="number" id="number" />
</div>
<div><button id="create">创建</button>
</div>
JavaScript:
// 创建数组收集canvas,防止被垃圾回收
let canvasQueue = [];
// 创建 1MB canvas
const create1MCanvas = () => {const size = 512;const canvas = document.createElement("canvas");canvas.width = size;canvas.height = size;const context = canvas.getContext("2d");context.fillRect(0, 0, size, size);return canvas;
};
// 创建 n 个 1MB 的 canvas
const createNMCanvas = (n) => {for (let i = 0; i < n; i++) {canvasQueue.push(create1MCanvas());}
};
const input = document.querySelector("#number");
const button = document.querySelector("#create");
button.addEventListener("click", (event) => {event.preventDefault();const number = input.value;if (!Number.isNaN(Number(number))) {canvasQueue = [];createNMCanvas(Number(number));console.log(`创建${number}MB canvas成功`);}
});
接下来,我们创建 1000 个 canvas
,也就是增加 1GB 内存,看看 GPU 的内存占用情况~
嗯,如你所见,内存占用确实妥妥增加 1GB 的内存,canvas
对内存确实存在肉眼可见的影响,canvas
可存放多少就取决于计算机设备的情况了,只要设备足够 nb,就能吃得下。比如我的笔记本设备是 16GB 的运行内存,当我增加 10GB 的canvas
时,页面就开始出现卡顿,这说明此时的计算机面对 10GB 内存的增加显得有些吃力。
canvas 清除行动
因为上面的例子中,每次循环都创建了 canvas
变量,并且通过数组收集,使其不被回收。JS 会在创建变量时为其自动分配内存,在不使用的时候自动释放内存,释放过程就是「 垃圾回收机制」。
既然 canvas
没有被回收,那么我们怎么阻止 canvas
带来的内存消耗呢?
首先,我们只创建一个 canvas
,每次循环在画布上绘制完图像后,对画布进行清理。并且去除掉对 canvas
的收集,使其被自动回收,因为在实际开发场景中,在图像绘制完成后我们已经拿到了想要的东西,canvas
已经没啥用了,可以抛弃了。
修改代码如下:
let canvasQueue = [];
let canvas = document.createElement("canvas");
const size = 512;
canvas.width = size;
canvas.height = size;
const context = canvas.getContext("2d");
// 创建 n x 1MB canvas
const createNMCanvas = (n) => {for (let i = 0; i < n; i++) {context.fillRect(0, 0, size, size);// 绘制完后,在这里去拿我们想要的数据 如转换后的 base64 编码// context.drawImage(image, 0, 0, w, h);// const base64 = canvas.toDataURL('image/png')// 清除画布context.clearRect(0, 0, size, size);}
};
此时,我们增加难度,创建 2GB 的 canvas
,我们可以发现对计算机的内存占用几乎毫无波澜,并且页面也不会出现卡顿或者崩溃的现象。
写在最后
在实际开发中,我们更多是利用 canvas
来对图片做一些转换操作,这对内存并无影响,但如果创建 canvas
数量多的话,一定要注意 canvas
的最大内存限制问题。很多不起眼的代码,对内存却有着肉眼可见的影响,在开发中多注意 CPU、内存的使用情况和性能指标,有利于增加我们代码的健硕性和稳定性。
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取