一点微小的工作,初步实现无刷新加载。
本教程仅在Fluid主题1.8.1版本测试通过。

一、介绍

pjax是什么?

pjax 结合 pushState 和 ajax 技术(PJAX=history.pushState+ajax), 不需要重新加载整个页面就能从服务器加载 Html 到你当前页面,这个 ajax 请求会有永久链接、title 并支持浏览器的回退/前进按钮。

简单来说pjax就是无刷新加载,看起来似乎很高端,但其实很多网站都已经在使用和pjax类似的技术,比如百度搜索:在搜索页更换关键词搜索,页面只是部分内容重载。

pjax的优点和好处很多,仅就个人博客而言,最直观的体验就是能够实现页面平滑切换和音乐不间断播放

pjax效果演示:http://pjax.herokuapp.com
~ 或者就在本站体验。 ~

pjax这般优秀,奈何本站使用的如此好看的Fluid主题竟然不支持,只好自己动手,慢慢摸索和折腾一下。

二、安装

1、版本选用

ONE.jquery-pjax
TWO.Moox-pjax

第一个是需要使用jquery依赖的版本,俩版本大同小异,只是后者代码更新日期较新,在这里我选用了后者。

推荐首先阅读下官方文档

2、引入js

/主题目录/layout/partial/footer.ejs中加入

<script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.js"></script>

来引入pjax;

或者使用npm安装本地后引用

$ npm install pjax
<script src="./node_modules/pjax/pjax.js"></script>

现在,我们已经安成了pjax的安装,本地预览一下,页面跳转已初见成效。但你可能会发现,标题消失、图片失效等等各项功能异常。别着急,这是由于部分js未能加载导致的,稍后会解决。

三、使用

1、功能配置

/主题目录/layout/layout.ejs中插入代码:

<script>
    // pjax
    var pjax = new Pjax({
    selectors: [
      "title", //加载标题
      "main", //加载主体框架部分
      "header", //加载头部(文章页头图等)
    ]
   })
</script>

elements选择触发器,即点击什么来触发pjax(只能用a或者form),a为链接。
selectors根据css选择器来选择更新的节点(即当触发elements时哪些内容会刷新,其余部分保持不变)

有用的pjax函数

pjax.loadUrl();
document.addEventListener('pjax:send', function () {});
document.addEventListener('pjax:complete', function () {});
document.addEventListener('pjax:error', function () {});

2、项目优化

2.1 Valine评论重载

在Fluid1.8.1版本中已经解决了 pjax刷新后发现评论不能加载 这个问题,如果使用1.8.0及以下版本需要进行下面的操作。
/主题目录/layout/layout.ejs中插入代码:

//加载valine
function LoadValine(){
      $.getScript("https://cdn.staticfile.org/valine/1.4.14/Valine.min.js", function() {
          
        // 生成评论的代码
        var GUEST_INFO = ['nick','mail','link'];
        var guest_info = '<%= theme.valine.meta %>'.split(',').filter(function(item){
          return GUEST_INFO.indexOf(item) > -1
        });
        var valine = new Valine();
        valine.init({
          el: "#vcomments",
          guest_info: guest_info,
          app_id: "<%= theme.valine.appid %>",
          app_key: "<%= theme.valine.appkey %>",
          placeholder: "<%= theme.valine.placeholder %>",
          path: <%= theme.valine.path %>,
          avatar: "<%= theme.valine.avatar %>",
          meta: <%- JSON.stringify(theme.valine.meta || []) %>,
          pageSize: "<%= theme.valine.pageSize %>",
          lang: "<%= theme.valine.lang %>",
          highlight: <%= theme.valine.highlight %>,
          recordIP: <%= theme.valine.recordIP %>,
          serverURLs: "<%= theme.valine.serverURLs %>"
        })
    });
  };

然后进行这个函数的重载

document.addEventListener('pjax:complete', function (){
    LoadValine();
});

如果在自建页面使用了Valine评论,仍需要添加//加载valine函数,但是请将重载指令放在评论所在页面的.md文件中,避免出现评论重复加载现象。同时为了保证在该页面刷新时重载评论,需要使用pjax的load函数。
完整示例如下:

<!-- pjax完成/监听刷新加载Valine -->
<script>
document.addEventListener('pjax:complete', function (){
   LoadValine();
 });
window.addEventListener('load',function(){
   LoadValine();
 });
</script>

然而,这样操作以后,还是会出现一些bug:在文章页评论加载完毕以后,用户跳转留言页,再返回到刚刚访问的文章页时,会出现评论重复的现象。我还在寻找修复方法,但是一般访问情景应该不会有特别大的影响。
(谁会在页面之间反复横跳呢?)

2.2 懒加载修复

/主题目录/layout/layout.ejs中添加重载函数如下:

    document.addEventListener('pjax:complete', function (){
      $.getScript("/js/lazyload.js");
});

2.3 下滑箭头、抖动等修复

/主题目录/layout/layout.ejs中插入代码:

document.addEventListener('pjax:complete', function (){
  $.getScript("/js/main.js");
  $.getScript("/js/debouncer.js");
});

// 窗口监听load(加载、刷新)事件,执行函数
window.addEventListener('load',function(){
 $.getScript("/js/main.js");
});

2.4 FancyBox重载

/主题目录/layout/layout.ejs中插入代码:

  //加载FancyBox
  function LoadFancyBox(){
$('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
     function () {
       var element = document.createElement('a');
       $(element).attr('data-fancybox', 'images');
       $(element).attr('href', $(this).attr('src'));
       $(this).wrap(element);
     }
   );
  };

然后进行这个函数的重载

document.addEventListener('pjax:complete',
function (){
     LoadFancyBox();
});

bug:在关闭放大的图片后,页面会漂移。
我暂时关闭了这个功能。

2.5 修复MarkDown样式丢失

Fluid1.8.1的更新中,强哥加入判断以减少不必要的加载项,但加入pjax之后判断似乎失效了,那么就干脆不要判断好了(反向升级)
/主题目录/layout/_partial/css.ejs中的<% if (is_post() || is_page()) { %>判断删除即可。

2.6 加入加载动画

css如下:

.loading{display:none}
.loading{height:100%;width:100%;position:fixed;top:0;left:0;z-index:999999;background-color:rgba(250,250,250,.9)}
.loading img{width: 280px;height:210px;position: relative;top: 45%;left: 50%;margin-left:-140px;margin-top: -105px;}
#loader{display: block; position: relative; left: 50%; top: 50%; width: 150px; height: 150px; margin: -75px 0 0 -75px; border-radius: 50%; border: 3px solid transparent; border-top-color: #ff5a5a; -webkit-animation: spin 1s linear infinite; animation: spin 1s linear infinite;}
#loader:before{content: ""; position: absolute; top: 5px; left: 5px; right: 5px; bottom: 5px; border-radius: 50%; border: 3px solid transparent; border-top-color: #5af33f; -webkit-animation: spin 3s linear infinite; animation: spin 3s linear infinite;}
#loader:after{content: ""; position: absolute; top: 15px; left: 15px; right: 15px; bottom: 15px; border-radius: 50%; border: 3px solid transparent; border-top-color: #6dc9ff; -webkit-animation: spin 2s linear infinite; animation: spin 2s linear infinite;}
@-webkit-keyframes spin{0%{-webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg);} 100%{-webkit-transform: rotate(360deg); -ms-transform: rotate(360deg); transform: rotate(360deg);}}
@keyframes spin{0%{-webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg);} 100%{-webkit-transform: rotate(360deg); -ms-transform: rotate(360deg); transform: rotate(360deg);}}

pjax载入动画:

  // 开始 PJAX 执行的函数
  document.addEventListener('pjax:send', function (){
$(".loading").css("display", "block");//加载转圈圈的css
 });

 // PJAX 完成之后执行的函数,可以和上面的重载放在一起
 document.addEventListener('pjax:complete', function (){
$(".loading").css("display", "none");

另外推荐一下Nprogress.js也很好看。

四、本站pjax完整代码

<script>
  // pjax
  var pjax = new Pjax({
  selectors: [
    "title",
    "main",
    "header",
  ]
 })
    
        //加载valine
 function LoadValine(){
    $.getScript("https://cdn.staticfile.org/valine/1.4.14/Valine.min.js", function() {
        
      // 生成评论的代码
      var GUEST_INFO = ['nick','mail','link'];
      var guest_info = '<%= theme.valine.meta %>'.split(',').filter(function(item){
        return GUEST_INFO.indexOf(item) > -1
      });
      var valine = new Valine();
      valine.init({
        el: "#vcomments",
        guest_info: guest_info,
        app_id: "<%= theme.valine.appid %>",
        app_key: "<%= theme.valine.appkey %>",
        placeholder: "<%= theme.valine.placeholder %>",
        path: <%= theme.valine.path %>,
        avatar: "<%= theme.valine.avatar %>",
        meta: <%- JSON.stringify(theme.valine.meta || []) %>,
        pageSize: "<%= theme.valine.pageSize %>",
        lang: "<%= theme.valine.lang %>",
        highlight: <%= theme.valine.highlight %>,
        recordIP: <%= theme.valine.recordIP %>,
        serverURLs: "<%= theme.valine.serverURLs %>"
      })
  });
};

      //pjax开始时加载函数
    document.addEventListener('pjax:send', function (){
  $(".loading").css("display", "block");//加载转圈圈的css
   });
    
     // pjax加载结束后执行函数
    document.addEventListener('pjax:complete', function (){
    $(".loading").css("display", "none");
      $.getScript("/js/lazyload.js");
      $.getScript("/js/main.js");
      $.getScript("/js/debouncer.js");
    });
    
    // 窗口监听load(加载、刷新)事件,执行函数
    window.addEventListener('load',function(){
     $.getScript("/js/main.js");
    });
    
</script>

五、To Do List

以下功能暂时还没有修复,后期慢慢研究,也期待有大佬提供解决方案。

代码高亮和复制按钮
Valine评论重复问题
FancyBox漂移问题
Typed.js文本打字机
Toc.js文章目录
anchor锚点
其他评论系统
Editor.md编辑器

六、后记

参考文章:
https://www.jianshu.com/p/557cad38e7dd
https://inkss.cn/article/other/80b5f235.html
感谢:lluuiqMoMik 对本站pjax实现过程中的指导和帮助。
由于个人能力实在有限,导致bug多多,还是期待Fluid官方支持pjax。