页面含多个Swiper实例时调用swiper.destroy()方法触发报错
问题描述
同一页面存在多个Swiper实例时,需要根据屏幕宽度动态完成滑块的销毁与初始化操作,核心实现代码如下:
const mobile = window.matchMedia('(max-width: 575.98px)'); function instantiateGridSlider() { const sliders = []; $('.thumbnails-grid').each(function(index, element){ const instance = $(this).data('instance'); sliders[index] = new Swiper(element, { speed: 500, spaceBetween: 30, slidesPerView: 1, loop: true, init: false, pagination: { el: '.swiper-pagination-' + instance, type: 'bullets', clickable: true, }, }); }); return sliders; } function initGridSlider() { if ($('.thumbnails-grid').length == 0) { return; } const gridSliders = instantiateGridSlider(); for (let i = 0; i < gridSliders.length; i++) { if (mobile.matches) { gridSliders[i].init(gridSliders[i].el); } else { gridSliders[i].destroy(true, true); } } } window.addEventListener('resize', function() { initGridSlider(); }); initGridSlider();
调整窗口尺寸尝试销毁滑块时,触发如下报错:
Uncaught TypeError: Cannot read properties of undefined (reading 'children')
at Swiper.loopDestroy (loopDestroy.js:6:1)
at Swiper.destroy (core-class.js:545:1)
at initGridSlider (slider.js:84:1)
at slider.js:90:1
报错在执行loopDestroy逻辑时抛出,需要定位问题成因并获取可行修复方案。
问题成因
- 每次触发
initGridSlider都会重新创建一批全新的Swiper实例,从未清理过之前生成的旧实例。loop模式下Swiper初始化时会自动复制前后slide生成循环DOM结构,重复创建实例会导致DOM结构和实例内部状态完全不匹配。 - 调用
destroy方法前没有判断实例的初始化状态:如果实例创建时设置了init:false且从未执行过初始化,直接调用销毁方法会触发loop模块的清理逻辑,此时实例内部没有存储loop生成的DOM引用,访问children属性时就会拿到undefined抛出错误。
修复方案
- 全局缓存已创建的Swiper实例,不要每次resize都重复新建实例
- 执行销毁逻辑前先判断实例是否已完成初始化,未初始化的实例不需要调用destroy
- 断点切换时先清理所有已存在的旧实例,再根据当前屏幕宽度判断是否需要初始化新实例
- 增加resize防抖,避免窗口拖拽调整过程中频繁触发实例的创建、销毁逻辑
修复后的完整代码:
const mobile = window.matchMedia('(max-width: 575.98px)'); // 全局缓存Swiper实例 let gridSliders = []; function destroyAllSliders() { gridSliders.forEach(slider => { // 仅对已初始化的实例执行销毁操作 if (slider.initialized) { slider.destroy(true, true); } }); // 清空实例缓存 gridSliders = []; } function initGridSlider() { if ($('.thumbnails-grid').length === 0) { return; } // 每次状态切换前先清理所有旧实例 destroyAllSliders(); // 仅在移动端断点下创建并初始化Swiper if (mobile.matches) { $('.thumbnails-grid').each(function(index, element){ const instance = $(this).data('instance'); const slider = new Swiper(element, { speed: 500, spaceBetween: 30, slidesPerView: 1, loop: true, pagination: { el: '.swiper-pagination-' + instance, type: 'bullets', clickable: true, }, }); gridSliders.push(slider); }); } } // resize防抖,避免频繁触发逻辑 let resizeTimer = null; window.addEventListener('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(initGridSlider, 200); }); // 首次加载执行初始化 initGridSlider();
如果使用的是Swiper 6及以上版本,更推荐直接使用组件自带的breakpoints配置项实现不同宽度下的效果切换,不需要手动编写销毁、初始化逻辑,组件内部会自动处理断点切换的状态更新。
内容的提问来源于stack exchange,提问作者marcelo2605
相关产品推荐
相关产品推荐

