Do dung lượng bộ nhớ khả dụng trên thiết bị iOS có hạn, trình duyệt Safari trên iPhone và iPad áp dụng các ràng buộc nghiêm ngặt hơn so với phiên bản desktop — đặc biệt là đối với tổng dung lượng dữ liệu hình ảnh mà một trang HTML được phép tải.
Khi tổng kích thước hình ảnh đạt khoảng 8–10 MB, Safari di động sẽ ngừng tải thêm hình ảnh mới, và trong một số trường hợp nghiêm trọng, thậm chí có thể dẫn đến sự cố sập trình duyệt.
Mặc dù đa số website không chạm tới ngưỡng này nhờ thiết kế tối ưu, nhưng những ứng dụng web sử dụng nhiều hình ảnh — như thư viện ảnh lớn, slideshow tương tác, hoặc giao diện mô phỏng hiệu ứng chuyển cảnh kiểu Flipboard — rất dễ gặp phải giới hạn này.
Ban đầu, nhiều người cho rằng chỉ cần xóa phần tử <img> khỏi DOM là đủ để giải phóng bộ nhớ:
const img = document.getElementById('oldImage');
img.parentNode.removeChild(img);
Tuy nhiên, cách này không hiệu quả. Dữ liệu hình ảnh gốc vẫn được giữ lại trong bộ nhớ, bất chấp việc phần tử đã bị gỡ bỏ khỏi cây DOM.
Giải pháp thực sự hoạt động là gán lại thuộc tính src của phần tử <img> sang một hình ảnh nhỏ (hoặc trống):
const img = document.getElementById('oldImage');
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
Việc thay đổi src giúp trình duyệt nhận diện rằng tài nguyên cũ không còn được tham chiếu và có thể được thu dọn bởi cơ chế garbage collection.
Khi triển khai kỹ thuật này, cần lưu ý ba điểm quan trọng:
- Dữ liệu hình ảnh không được giải phóng ngay lập tức sau khi đổi
src. Cần một khoảng thời gian để garbage collector thực hiện dọn dẹp. Do đó, nếu liên tục tải hình ảnh mới quá nhanh, bạn vẫn có thể vượt quá giới hạn bộ nhớ. - Một khi Safari đã đạt ngưỡng giới hạn, nó sẽ từ chối tải thêm bất kỳ hình ảnh nào — kể cả sau khi bạn đã xóa bớt tài nguyên hoặc điều hướng sang trang khác. Trong quá trình kiểm thử, bạn thường xuyên phải khởi động lại Safari để đặt lại trạng thái.
- Nếu bạn định xóa phần tử
<img>khỏi DOM, hãy đảm bảo rằng việc gán lạisrcđược thực hiện trước khi phần tử bị loại bỏ hoàn toàn. Nếu không, tham chiếu đến hình ảnh gốc có thể bị mất trước khi trình duyệt kịp giải phóng tài nguyên.
Một cách tiếp cận an toàn là:
const img = document.getElementById('oldImage');
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
img.parentNode.removeChild(img);
// Giữ tham chiếu đủ lâu để đảm bảo giải phóng
setTimeout(() => {
// img = null; (không bắt buộc nếu không có closure giữ tham chiếu)
}, 60000);
Lưu ý rằng chuỗi base64 ở trên là một GIF trong suốt 1×1 pixel — thường được dùng làm "placeholder rỗng" để thay thế hình ảnh gốc mà không tiêu tốn bộ nhớ đáng kể.
Kỹ thuật này đã được tích hợp vào thư viện Zepto thông qua module assets, giúp quản lý tài nguyên hình ảnh hiệu quả hơn trên các thiết bị iOS.