Jssor Slider加载360张Base64图片及大视频内存溢出问题求助
解决Jssor Slider渲染大量Base64图片导致的内存溢出问题
问题背景
我在使用Jssor Slider做轮播时遇到了内存溢出问题:需要渲染360张Base64格式图片+一个2GB本地视频,运行约1小时后图片无法显示,但视频播放正常。通过Chrome性能面板确认是内存占用过高导致的。
我的实现逻辑大概是:图片切换用了多段复杂动画(缩放、旋转、位移),通过JSONP远程拉取Base64数据并提前加载,不足360张时循环复用;视频是本地文件,播放正常。相关代码片段如下:
动画配置与幻灯片初始化
for (var i = 0; i < list_size; i++) { col_transitions[i] = [ { b: 0, d: 4000, sX: 2, sY: 2, e: { sX: 13, sY: 13 } }, { b: 4000, d: 3000, r: 360, e: { r: 4 } }, { b: 7000, d: 3000, r: -360, e: { r: 4 } }, { b: 10000, d: 4000, sX: -1.8, sY: -1.8, e: { sX: 13, sY: 13 } }, { b: 14000, d: 1600, x: 100, y: 70 }, { b: 15600, d: 1600, x: -150, y: -20 }, { b: 17200, d: 1600, x: 50, y: -50, sX: -0.2, sY: -0.2 } ]; $("#slick_list").find("div[data-u=slides]").append("<div><img data-u=\"image\" data-t=\"" + i + "\"/></div>"); } // 视频幻灯片添加 $("#slick_list").find("div[data-u=slides]").append( "<div style=\"position:relative;\">"+ "<video class=\"video1\" onended=\"video_end(1)\">"+ "<source src=\"videos\/s1.mov\" type=\"video\/mp4\">"+ "<\/video>"+ "<img src=\"\"/>"+ "<\/div>" );
图片循环复用逻辑
if(max_page !== null){ page = img_cnt%(max_page+1); }else{ page = img_cnt; }
图片加载与显示函数
function load_image(){ var img_path; if(max_page !== null){ page = img_cnt%(max_page+1); }else{ page = img_cnt; } if(eval("typeof img_dataArr._"+page)==="undefined"){ $.ajax({ async:false, cache :false, url : "{address}", jsonpCallback:"CB", crossDomain:true, method:"get", dataType :"jsonp", success:function(data){ var image_data = decodeURIComponent(data['file']); eval("img_dataArr._"+page+"=\"data:image/png;base64,\"+image_data"); if(data['max_cnt']) { max_page = page; } }, error:function(e){ max_page = page; }, complete:function(data){ if(!((img_cnt-2)<0)){ $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt-2).attr('src',''); }else{ $('#slick_list').find("div[data-u=slides]").last().find('img').eq(360+img_cnt-2).attr('src',''); } $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt).attr('src',eval("img_dataArr._"+page)); $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt).css("display","inline"); img_cnt = img_cnt+1; } }); }else{ if(!(((img_cnt%list_size)-2)<0)){ $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt%list_size-2).attr('src',''); }else{ $('#slick_list').find("div[data-u=slides]").last().find('img').eq(360+img_cnt%list_size-2).attr('src',''); } $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt%list_size).attr('src',eval("img_dataArr._"+page)); $('#slick_list').find("div[data-u=slides]").last().find('img').eq(img_cnt%list_size).css("display","inline"); img_cnt = img_cnt+1; } }
问题根源分析
从代码和现象来看,内存溢出主要来自这几个点:
- Base64图片内存未有效释放:虽然你把旧图片的
src设为空,但浏览器对Base64图片的垃圾回收很迟钝,尤其是当图片元素还在DOM里、或者被动画逻辑引用时,内存根本收不回来。 eval滥用导致内存泄漏:用eval给img_dataArr动态加属性,会生成无法被JS引擎优化的闭包,这些闭包会一直占用内存,没法被回收。- DOM元素太多:一次性创建360个幻灯片容器,哪怕图片没显示,DOM本身就占内存,再加上Jssor要维护每个幻灯片的动画状态,内存只会越堆越多。
- 同步AJAX阻塞垃圾回收:
async:false会卡死浏览器事件循环,垃圾回收进程没法及时运行,内存堆积速度更快。
具体修复方案
1. 改用幻灯片复用池,减少DOM数量
别一次性创建360个幻灯片,只保留当前、上一个、下一个3个容器循环复用,这样DOM元素数量固定,不会持续增长。示例代码:
// 初始化时只创建3个可复用的幻灯片容器 const slidePool = []; for (let i = 0; i < 3; i++) { const slide = $("<div><img data-u=\"image\"/></div>"); $("#slick_list").find("div[data-u=slides]").append(slide); slidePool.push(slide); } // 视频幻灯片保留原逻辑 $("#slick_list").find("div[data-u=slides]").append( "<div style=\"position:relative;\">"+ "<video class=\"video1\" onended=\"video_end(1)\">"+ "<source src=\"videos\/s1.mov\" type=\"video\/mp4\">"+ "<\/video>"+ "<\/div>" ); // 切换时获取复用的幻灯片容器 function getCurrentSlide(index) { return slidePool[index % 3]; }
2. 彻底清理图片内存
光清空src不够,得主动切断图片对象的引用,帮浏览器回收内存:
function clearImageMemory(imgElement) { // 先清空src imgElement.attr('src', ''); // 创建新的img元素替换旧的,彻底切断旧图片的所有引用 const newImg = $("<img data-u=\"image\"/>"); imgElement.replaceWith(newImg); return newImg; }
3. 把eval换成对象属性访问
eval是内存泄漏的重灾区,改用方括号访问属性:
// 原代码:eval("img_dataArr._"+page+"=\"data:image/png;base64,\"+image_data"); // 修改为: img_dataArr[`_${page}`] = `data:image/png;base64,${image_data}`; // 原代码:eval("img_dataArr._"+page) // 修改为: img_dataArr[`_${page}`]
4. 同步AJAX改异步
async:false会阻塞浏览器,导致垃圾回收延迟,改成异步请求:
$.ajax({ async: true, // 这里改成true cache: false, url: "{address}", jsonpCallback: "CB", crossDomain: true, method: "get", dataType: "jsonp", // 其他逻辑不变 });
5. 优化动画配置
你的每个幻灯片动画时长加起来快20秒,而且每个幻灯片都有独立的动画配置,Jssor要一直维护这些状态,内存消耗很大。可以:
- 简化动画效果,去掉不必要的关键帧
- 如果所有幻灯片动画相同,直接复用同一个动画配置,不用为每个
i创建新的col_transitions[i]
6. 调试时主动触发垃圾回收(生产环境慎用)
在Chrome开发者工具里开启"Enable JavaScript garbage collection"后,可以用window.gc()主动触发垃圾回收,用来验证修改后的内存回收是否有效,但生产环境别依赖这个。
验证方法
修改后用Chrome开发者工具的Memory和Performance面板验证:
- 录制内存快照,对比修改前后的内存占用
- 录制长时间运行的性能数据,看内存是否不再持续增长
- 用Memory的Retainers选项,检查有没有未被回收的图片对象或DOM元素
内容的提问来源于stack exchange,提问作者tryAll
相关产品推荐
相关产品推荐

