PJAX是一种用于加快网页加载速度的技术,结合了HTML5的pushState API和Ajax技术。使用PJAX可以实现局部页面内容的更新,减少不必要的页面刷新,提高用户体验和降低服务器负载。但也有一些局限性,如对于不支持HTML5 pushState API的浏览器可能无法正常工作。在WordPress主题中配置PJAX时需要解决一些问题,如Matomo追踪代码、左侧目录刷新、Lazyload等。|
powered by RippleAi
本文最后更新于 343 天前,其中的信息可能已经有所发展或改变。
近期为自己的wp主题配置了PJAX~此前使用的是Turbolinks插件,因为插件属于不可控元素,产生过多BUG后还是决定手动配置更为先进的PJAX(其实BUG更多了,但至少可以逐一排查...)
PJAX原理※
PJAX(Pushstate + Ajax)是一种用于加快网页加载速度的技术。它结合了HTML5的pushState API和Ajax技术,使得在不刷新整个页面的情况下,可以实现局部页面内容的更新。PJAX的主要原理可以分为以下几个步骤:
- 用户点击链接:当用户点击一个链接时,PJAX会拦截该链接的点击事件。
- 阻止默认行为:PJAX会阻止浏览器执行默认的链接跳转行为,避免整个页面的刷新。
- 发送Ajax请求:接着,PJAX会通过Ajax发送一个请求到服务器,请求目标链接的内容。这个请求通常会包含一个特殊的请求头,以便服务器能够识别这是一个PJAX请求,从而仅返回局部更新所需的内容,而非整个页面的HTML代码。
- 更新URL和页面内容:当服务器返回局部内容后,PJAX会利用HTML5的pushState API来更新浏览器地址栏中的URL,然后用返回的内容替换页面中指定的容器元素。这样一来,用户就会感觉像是进行了一次正常的页面跳转,但实际上只是局部内容发生了更新。
- 更新浏览器历史记录:通过pushState API更新URL之后,浏览器的历史记录也会相应地更新。这样,用户在点击浏览器的前进和后退按钮时,可以按照预期的方式导航。
PJAX的优势在于减少了不必要的页面刷新,提高了用户体验。同时,由于只需要加载局部内容,它还有助于降低服务器负载。然而,PJAX也有一些局限性,例如对于不支持HTML5 pushState API的浏览器,PJAX可能无法正常工作。
引入PJAX※
引入JS文件※
前往主题的function.php,添加如下代码入队:
function add_pjax() {
wp_register_script('pjax', 'https://cdn.bootcss.com/jquery.pjax/2.0.1/jquery.pjax.min.js', array('jquery'), '2.0.1', true);
wp_enqueue_script('pjax');
}
add_action('wp_enqueue_scripts', 'add_pjax');
PHP
包裹PJAX容器※
本段部分参考至https://alpha.skywt.cn/post/typecho-pjax-and-some-problems
F12检查自己博客的html结构,找到需要PJAX动态刷新的类,并将它包裹在PJAX容器中:
一般的网页结构:
<html>
<head>
<!-- ... -->
</head>
<body>
<header><!-- 页眉,标题什么的 --></header>
<nav><!-- 导航栏什么的 --></nav>
<main id="pjax-container">
<!-- 网站主体内容,文章列表 / 文章内容 / 评论什么的 -->
</main>
<script>
// 下面要加上的代码
</script>
<footer><!-- 页脚什么的 --></footer>
</body>
</html>
HTML
只需前往header.php以及footer.php中找到相应部分即可,在PJAX容器后方添加初始化代码(这里是Wordpress版本):
<script>
jQuery(document).ready(function($) {
$(document).pjax('a[href^="<?php echo home_url(); ?>"]:not(a[target="_blank"])', {
container: '#pjax-container',
fragment: '#pjax-container'
});
$(document).on('pjax:send', function() {
$('#pjax-container').fadeTo(700,0.0); //这里添加了一个淡入动画
// alert('开始加载');
// 开始加载时要运行的代码(如显示加载动画)
});
$(document).on('pjax:complete', function() {
$('#pjax-container').fadeTo(700,1);
// 完成加载时要运行的代码
});
});
</script>
HTML
保存并清除缓存后,刷新后打开开发者工具,网络选项搜索PJAX,看见?_pjax=#pjax-container意味着PJAX配置成功。
加载指示器※
引入PJAX时,用户点击超链后浏览器将不作回应,因此需要使用加载指示器显示页面正在加载中,HTML中加入
<div id="loading-spinner" class="loader" style="display:none;"></div>
HTML
对应CSS(这里使用CSS矢量动画作为加载指示)
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loading-spinner {
position: fixed;
top: 20px;
right: 20px;
width: 24px;
height: 24px;
border: 2px solid #ccc;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 9999;
}
CSS
PJAX发送时,加载动画出现,完成时隐藏,这是我的完整JS
:
jQuery(document).ready(function ($) {
$(document).pjax('a[href^="<?php echo home_url(); ?>"]:not(a[target="_blank"])', {
container: '#pjax-container',
fragment: '#pjax-container'
});
$(document).on('pjax:send', function () {
// 显示加载指示器
$('#loading-spinner').show();
window.scrollTo({ top: 0, behavior: 'smooth' });
$('#pjax-container').fadeTo(400, 0.0);
$('#toc').empty();
});
$(document).on('pjax:complete', function () {
// 隐藏加载指示器
$('#loading-spinner').hide();
// 初始化左侧小组件
Toc.init({
$nav: $('#toc')
});
$('body').scrollspy({
target: '#toc'
});
trackPjaxPageView();
$('#pjax-container').fadeTo(400, 1);
});
});
JavaScript
遇到的一些问题※
PJAX意味着随之而来的无数BUG...
Matomo追踪代码※
如果启用了Matomo或其他的一些追踪代码(它们一般放在Header内),意味着用户通过PJAX进入的界面无法被记入统计数据,因此必须在PJAX完成后重新载入这段JS,首先定义一个新函数
function trackPjaxPageView() {
if (typeof _paq !== 'undefined') {
_paq.push(['setReferrerUrl', document.referrer]);
_paq.push(['setDocumentTitle', document.title]);
_paq.push(['setCustomUrl', window.location.href]);
_paq.push(['trackPageView']);
}
}
JavaScript
PJAX完成后调用它:
$(document).on('pjax:complete', function() {
trackPjaxPageView();
$('#pjax-container').fadeTo(700,1);
});
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//analy.hiripple.com/";//这是我的matomo地址
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
JavaScript
左侧目录刷新※
将PJAX容器包裹在正文,意味着左侧小工具栏所有元素都不会刷新(例如这个使用了bootstrap-toc.js的小目录)
$(document).on('pjax:complete', function() {
// 初始化左侧小组件,查询官方的说明文档
Toc.init({
$nav: $('#toc'),
$scope: $(document.body)
});
$('body').scrollspy({
target: '#toc'
});
$('#pjax-container').fadeTo(700,1);
});
JavaScript
即使重新初始化,也存在前一界面目录未清除的问题,在发送PJAX时清除目录:
$(document).on('pjax:send', function() {
$('#pjax-container').fadeTo(700,0.0);
$('#toc').empty(); //清除目录
});
JavaScript
Lazyload/lightbox※
PJAX可能会导致Lazyload无法加载的情况:
function pjax_reload() {
var imgs = document.querySelectorAll("#main img.lazyload");
lazyload(imgs);
}
jQuery(document).ready(function($) {
// 初始化fancybox
jQuery('[data-fancybox="gallery"]').fancybox();
// 初始化lazyload
pjax_reload();
$(document).pjax('a[href^="<?php echo home_url(); ?>"]:not(a[target="_blank"])', {
container: '#pjax-container',
fragment: '#pjax-container'
});
$(document).on('pjax:send', function() {
$('#pjax-container').fadeTo(700,0.0);
});
$(document).on('pjax:complete', function() {
jQuery('[data-fancybox="gallery"]').fancybox();
// 重新初始化lazyload
pjax_reload();
$('#pjax-container').fadeTo(700,1);
});
});
JavaScript
或者直接使用图片loading='lazy'
属性,这是HTML5中的原生懒加载。原生懒加载不需要额外的处理,浏览器会自动处理懒加载。
点击事件※
这里需要切换夜间模式
function initDarkModeToggle() {
const toggleBtnMoon = document.getElementById("moon-icon");
const toggleBtnSun = document.getElementById("sun-icon");
function applyDarkMode() {
document.body.classList.add("dark-mode");
localStorage.setItem("theme", "dark-mode");
toggleBtnMoon.style.display = "none";
toggleBtnSun.style.display = "block";
}
function removeDarkMode() {
document.body.classList.remove("dark-mode");
localStorage.setItem("theme", "light-mode");
toggleBtnMoon.style.display = "block";
toggleBtnSun.style.display = "none";
}
if (localStorage.getItem("theme") === "dark-mode") {
applyDarkMode();
} else {
removeDarkMode();
}
toggleBtnMoon.addEventListener("click", applyDarkMode);
toggleBtnSun.addEventListener("click", removeDarkMode);
}
$(document).on('pjax:complete', function() {
initDarkModeToggle();
});
JavaScript