书到用时方恨少,技术到应用时才觉得自己有多菜鸟。为了使自己的网站速度快一点,刷新更加流畅一点,我也在自己网站上使用了一下 pjax,但因为一知半解,在刷新 splide 横幅时遇到了各种问题。表现如下:

一是从其他页面再点击链接回到横幅所在页面时,横幅不显示。

pjaxq1.webp

二是从横幅页面切换到其他页面后,然后(不刷新)点击浏览器的“转到上一页”按扭时,横幅下方的导航按钮虽然正常,但是点击没有反应。

pjaxq2.webp

三是从横幅页面切换到其他页面后,然后(不刷新)点击浏览器的“转到上一页”按扭时,横幅下方的导航按钮重复。
pjaxq3.webp

我最初的写法是:

<!-- splide 引入-->
<script src="<?php $this->options->themeUrl('splide/js/splide.min.js'); ?>"></script>
    <link href="<?php $this->options->themeUrl('splide/css/themes/splide-default.min.css'); ?>" rel="stylesheet">
    <link href="<?php $this->options->themeUrl('splide/css/splide.min.css'); ?>" rel="stylesheet">

 <!--- 刷新选择器部分 --->
<main>
    ...
    <!--- splide 实例的html部分 --->
    <section class="splide" aria-labelledby="">
        <div class="splide__track">
            <ul class="splide__list">
                <li class="splide__slide">Slide 01</li>
                <li class="splide__slide">Slide 02</li>
                <li class="splide__slide">Slide 03</li>
            </ul>
        </div>
    </section>
    ...
</main>

 <!--- pjax --->
<script>
    $(document).pjax(
        'a[href^="<?php Helper::options()->siteUrl() ?>"]:not(a[target="_blank"],a[no-pjax]), a[href^="?"]', {
            container: 'main',
            fragment: 'main',
            timeout: 8000
        }
    ).on('pjax:send', function() {
        NProgress.start(); //加载动画效果开始
    }).on('pjax:complete', function() {
        NProgress.done(); //加载动画效果结束
        //其他调用
        ...
    });
</script>

<!--- 创建横幅实例 --->
<script>
    new Splide('.splide').mount();
</script>

首先,我解决了问题一

发生问题一的原因是,pjax刷新处理后,没有重新初始化splide实例。于是,我在 pjax:complete 阶段再次写入了初始化splide实例方法。pjax 代码变成如下:

$(document).pjax(
        'a[href^="<?php Helper::options()->siteUrl() ?>"]:not(a[target="_blank"],a[no-pjax]), a[href^="?"]', {
            container: 'main',
            fragment: 'main',
            timeout: 8000
        }
    ).on('pjax:send', function() {
        NProgress.start(); //加载动画效果开始
    }).on('pjax:complete', function() {
        NProgress.done(); //加载动画效果结束
        //其他调用
        ...
        new Splide('.splide').mount();
    });  

可是这样,却导致了问题二的发生:从横幅页面切换到其他页面后,然后(不刷新)点击浏览器的“转到上一页”按扭时,横幅下方的导航按钮点击没有反应。

然后,我解决了问题二

发生问题二的原因是,浏览器前进后退时不会进行 pjax 请求,但会进行会按顺序执行如下事件:pjax:popstate、pjax:start、pjax:beforeReplace、pjax:end。于是,我将初始化splide实例方法写入到了 pjax:end 阶段,pjax 代码变成如下:

$(document).pjax(
        'a[href^="<?php Helper::options()->siteUrl() ?>"]:not(a[target="_blank"],a[no-pjax]), a[href^="?"]', {
            container: 'main',
            fragment: 'main',
            timeout: 8000
        }
    ).on('pjax:send', function() {
        NProgress.start(); //加载动画效果开始
    }).on('pjax:complete', function() {
        NProgress.done(); //加载动画效果结束
        //其他调用
        ...
    }).on('pjax:end', function() {
        new Splide('.splide').mount();
    });  

再测试浏览器后退,发现导航按钮重复了.只不过前面的导航按钮点击还是无效,而后面的导航按钮点击是有效的。

终于,我解决了问题三

经百度搜索“pjax 刷新 splide 时导航按钮重复”,终于发生问题三的原因是:pjax 的内容更新过程中,splide 实例没有正确地被销毁而被重新初始化了。解决这个问题的一个策略是,在每次 PJAX 内容更新后,先销毁现有的 splide 实例,然后再重新初始化。

依样画葫芦,我写入了初始化和销毁 splide 实例函数,并在 pjax:beforeSend 阶段加入了销毁splide实例方法,最终的代码如下:

<!-- splide 引入-->
<script src="<?php $this->options->themeUrl('splide/js/splide.min.js'); ?>"></script>
    <link href="<?php $this->options->themeUrl('splide/css/themes/splide-default.min.css'); ?>" rel="stylesheet">
    <link href="<?php $this->options->themeUrl('splide/css/splide.min.css'); ?>" rel="stylesheet">

 <!--- 刷新选择器部分 --->
<main>
    ...
    <!--- splide 实例的html部分 --->
    <section class="splide" aria-labelledby="">
        <div class="splide__track">
            <ul class="splide__list">
                <li class="splide__slide">Slide 01</li>
                <li class="splide__slide">Slide 02</li>
                <li class="splide__slide">Slide 03</li>
            </ul>
        </div>
    </section>
    ...
</main>

 <!--- pjax --->
<script>
    $(document).pjax(
        'a[href^="<?php Helper::options()->siteUrl() ?>"]:not(a[target="_blank"],a[no-pjax]), a[href^="?"]', {
            container: 'main',
            fragment: 'main',
            timeout: 8000
        }
    ).on('pjax:beforeSend', function() {
         destroySplide();  //销毁实例
    }).on('pjax:send', function() {
        NProgress.start(); //加载动画效果开始
    }).on('pjax:complete', function() {
        NProgress.done(); //加载动画效果结束
        //其他调用
        ...
    }).on('pjax:end', function() {
        initSplide();  //初始化实例
    });
</script>

<!--- 初始化和销毁横幅实例 --->
<script>
    let splide;
    function initSplide() {
        if (!splide) {
            splide = new Splide('.splide').mount();
        }
    }
    function destroySplide() {
        if (splide) {
            splide.destroy();
            splide = null;
        }
    }
    // 页面加载时初始化
    window.onload = initSplide;
    // 页面卸载前销毁
    window.onunload = destroySplide;
</script>