MoOx/Pjax 中文说明文档


# Pjax

MoOx/Pjax是由法国一位的开发者(MoOx)开源的项目(Github:https://github.com/MoOx/pjax )。

Pjax可以在任何网站上轻松启用快速Ajax导航 (使用 pushState() + XHR)。

PJAX是一个独立的JavaScript模块,使用Ajax(xmlhttpRequest)和pushState()提供快速浏览体验。

它允许您完全转换标准网站(服务器端生成的或静态的)的用户体验,让用户感觉自己在浏览应用程序,特别是对于那些带宽连接较低的用户。

不再重新加载整页。不再有多个HTTP请求。

PJAX不依赖其他库,如jquery或类似库。它完全是用普通JS写的。

引用此文件,你可以下载该项目,在自己的服务器上托管代码。或者是直接使用 JSDelivr 公共 CDN 友情提供的地址:。

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

建议将 JS 文件放在网站的底部,防止因文件加载过慢而导致的页面阻塞打开缓慢的情况。

文件引用:

<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.js"></script> (VERSION 为 版本号)

或者引用压缩版:

<script src="https://cdn.jsdelivr.net/npm/pjax@VERSION/pjax.min.js"></script> (VERSION 为 版本号)

您也可以从NPM安装PJAX:

npm install pjax

注意:如果使用此选项,则需要执行以下操作之一:

将脚本标记链接到pjax.js pjax.min.js 

<script src="./node_modules/pjax/pjax.js"></script>

使用类似bundler的Webpack。(index.js 不能在没有bundler的浏览器中使用)。

或者您可以克隆并使用npm从源代码构建bundle:

git clone https://github.com/MoOx/pjax.git
cd pjax
npm install
npm run build

然后将脚本标记链接到pjax.js pjax.min.js 

<script src="./pjax.min.js"></script>


 # PJAX做什么?

在引擎下,它只是一个带有 pushState() 调用的HTTP请求。

PJAX使用Ajax加载页面,并使用 pushState()  更新浏览器的当前URL,而无需重新加载页面布局或任何资源(JS、CSS),从而实现快速的页面加载。

它与所有permalink一起工作,可以更新页面的所有部分(包括HTML元、标题和导航状态)。

在浏览器不支持 history.pushState() 的情况下,PJAX优雅地降级,根本不做任何事情。

另外,Pjax:

  • 不限于一个容器,如jquery-pjax。

  • 完全支持浏览器历史记录(后退和前进按钮)。

  • 支持键盘浏览。

  • 自动返回到外部页面的标准导航(感谢明显船长的帮助)。

  • 对于没有适当的DOM tree的内部页面,自动返回到标准导航。

  • 允许CSS转换(动画)非常容易。

  • 仅在6KB左右(缩小和gzip)。

# PJAX如何工作

  • 它监听您想要的每一个点击链接(默认情况下,所有链接)。

  • 当单击内部链接时,PJAX通过Ajax获取页面的HTML。

  • PJAX呈现页面的DOM(不加载任何资源-图像、CSS、JS等)。

  • 检查所有定义的零件是否可以更换:

  • -如果页面不符合要求,则使用标准导航。

  • -如果页面满足要求,PJAX将执行所有定义的DOM替换。

  • 然后它使用 pushState() 更新浏览器的当前URL。

#概述

PJAX是全自动的。您不需要更改现有HTML的任何内容,您只需要指定在导航站点时要替换页面上的哪些元素。

以下面的页面结构为例。

<!DOCTYPE html>
<html>
<head>
  <!-- metas, title, styles, etc -->
  <title>My Cool Blog</title>
  <meta name="description" content="Welcome to My Cool Blog">
  <link href="/styles.css" rel="stylesheet">
</head>
<body>
  <header class="the-header">
    <nav>
      <a href="/" class="is-active">Home</a>
      <a href="/about">About</a>
      <a href="/contact">Contact</a>
    </nav>
  </header>
  <section class="the-content">
    <h1>My Cool Blog</h1>
    <p>
      Thanks for stopping by!
      <a href="/about">Click Here to find out more about me.</a>
    </p>
  </section>
  <aside class="the-sidebar">
    <h3>Recent Posts</h3>
    <!-- sidebar content -->
  </aside>
  <footer class="the-footer">
    &copy; My Cool Blog
  </footer>
  <script src="onDomReadystuff.js"></script>
  <script>
    // analytics
  </script>
</body>
</html>

我们希望PJAX拦截URL /about,代替 .the-content 请求的结果内容。

如果我们可以替换 <nav> 来显示 /about 链接处于活动状态,也会很好,以及更新我们的页面meta和 <aside> 边栏。

总之,我们想更新页面标题和meta,header,content,sidebar区域,不重新加载样式或脚本。

我们可以通过告诉PJAX监听所有的 a 标签(这是默认的)并使用上面定义的css选择器(而不忘记最小的meta),很容易做到这一点:

var pjax = new Pjax({
  selectors: [
    "title",
    "meta[name=description]",
    ".the-header",
    ".the-content",
    ".the-sidebar",
  ]
})

现在,当PJAX兼容浏览器中的某个人单击页面上的内部链接时,每个选择器的内容将替换为下一页内容中的特定内容片段。

魔术!真的!服务器端不需要做任何事情!

# 与jquery pjax的区别

  • 没有jquery依赖项。

  • 不限于容器。

  • 没有服务器端要求。

  • 适用于CommonJS环境(webpack/browserify),AMD(Requirejs)甚至全球。

  • 允许使用CSS动画进行页面转换。

  • 很容易调整,因为每个方法都是公共的(因此是可重写的)。

# 兼容性

PJAX只适用于支持 history.pushState() API的浏览器。当API不受支持时,PJAX将进入回退模式(它什么也不做)。

要查看浏览器是否支持PJAX,请使用 Pjax.isSupported() .

#用法

 new Pjax() 

让我们多谈谈最基本的开始方法。

在实例化PJAX时,可以将选项作为对象传递到构造函数中:

var pjax = new Pjax({
  elements: "a", // default is "a[href], form[action]"
  selectors: ["title", ".the-header", ".the-content", ".the-sidebar"]
})

这将在所有链接上启用pjax,并使用CSS选择器指定要替换的部件 "title", ".the-header", ".the-content", ".the-sidebar".

在某些情况下,您可能只希望针对某些特定元素来应用PJAX行为。在这种情况下,您可以做两件不同的事情:

1.使用自定义CSS选择器(如“a.js-pjax”“.js-pjax a”等)。

2.重写 Pjax.prototype.getElements .

*注意:如果这样做,请确保返回节点列表。

// use case 1
var pjax = new Pjax({ elements: "a.js-Pjax" })
// use case 2
Pjax.prototype.getElements = function() {
  return document.getElementsByClassName(".js-Pjax")
}
var pjax = new Pjax()

 loadUrl(href, [options])

使用此方法,可以手动触发加载URL:

var pjax = new Pjax()
// use case 1
pjax.loadUrl("/your-url")
// use case 2 (with options override)
pjax.loadUrl("/your-other-url", { timeout: 10 })

 handleResponse(responseText, request, href, options)

此方法获取原始响应,处理URL,然后调 pjax.loadContent() 将其实际加载到DOM中。

它通过以下参数传递:

  • responseText(string):这是原始响应文本。这相当于request.responseText .

  • request(XMLHttpRequest):这是XHR对象。

  • href (string): 这是传递给 loadUrl() 的URL。

  • options (object): 这是一个具有此请求选项的对象。该结构基本上与常规选项对象匹配,并具有一些额外的内部属性。

如果要在数据加载到DOM之前(而不是加载到DOM中)处理数据,可以重写此项。

例如,如果要检查非HTML响应,可以执行以下操作:

var pjax = new Pjax();
pjax._handleResponse = pjax.handleResponse;
pjax.handleResponse = function(responseText, request, href, options) {
  if (request.responseText.match("<html")) {
    pjax._handleResponse(responseText, request, href, options);
  } else {
    // handle non-HTML response here
  }
}

 refresh([el]) 

使用此方法将pjax绑定到初始化pjax时不存在的dom元素的子元素,例如由另一个库或脚本动态插入的内容。如果不带参数调用,PJAX将再次解析整个文档以查找新插入的元素。

// 在插入新的DOM内容后运行的回调或承诺中
var newContent = document.querySelector(".new-content");
pjax.refresh(newContent);

 reload() 

window.location.reload()  用于强制页面重新加载。

pjax.reload()

 Options 

elements (String, default: "a[href], form[action]")

用于查找要应用PJAX的链接的CSS选择器。如果需要多个特定的选择器,请用逗号分隔它们。

// 单一元素
var pjax = new Pjax({
  elements: ".ajax"
})
// 多元素
var pjax = new Pjax({
  elements: ".pjax, .ajax",
})

selectors (Array, default: ["title", ".js-Pjax"])

CSS选择器用于查找要替换的内容。

var pjax = new Pjax({
  selectors: [
    "title",
    "the-content",
  ]
})

如果查询返回多个项,则只保留索引。

您可以做的示例:

<!DOCTYPE html>
<html>
<head>
  <title>Page title</title>
</head>
<body>
  <header class="js-Pjax">...</header>
  <section class="js-Pjax">...</section>
  <footer class="the-footer">...</footer>
  <script>...</script>
</body>
</html>

这个例子是正确的,应该“按预期”工作。

注意:如果当前页面和新页面没有相同数量的DOM元素,PJAX将返回到正常的页面加载。

switches (Object, default: {} )

这是一个包含回调的对象,可用于用新元素切换旧元素。

对象键应该是已定义的选择器之一(从selectors 选择)。

var pjax = new Pjax({
  selectors: ["title", ".Navbar", ".js-Pjax"],
  switches: {
    "title": Pjax.switches.outerHTML, // default behavior
    ".the-content": function(oldEl, newEl, options) {
      // this is identical to the default behavior
      oldEl.outerHTML = newEl.outerHTML
      this.onSwitch()
    },
    ".js-Pjax": Pjax.switches.sideBySide
  }
})

回调绑定到PJAX实例本身,以允许您重用它( this.onSwitch()

开启回调

  • Pjax.switches.outerHTML: 默认行为,元素替换使用 outerHTML.

  • Pjax.switches.innerHTML: 元素替换使用 innerHTML 和复制 className 

  • Pjax.switches.replaceNode: 元素替换使用 replaceChild

  • Pjax.switches.sideBySide: 智能替换,允许您在使用CSS动画时将两个元素都置于同一父元素中。当所有子元素都已完全设置动画(将触发AnimationEnd事件)时,将删除旧元素。

创建回调

你的回调函数可以做你想做的任何事情,但是你需要:

1.以某种方式将旧( oldEl )内容替换为新( newEl )内容。

2.调用 this.onSwitch() 以触发附加的回调。

以下是默认行为的示例:

function(oldEl, newEl, pjaxOptions) {
  oldEl.outerHTML = newEl.outerHTML
  this.onSwitch()
}

switchesOptions (Object, default: {})

这些选项可以在通过交互替换内容时使用。目前,只有 Pjax.switches.sideBySide 可以用。当您使用诸如animate.css之类的东西时,无论有没有wow.js,这都非常方便。

var pjax = new Pjax({
  selectors: ["title", ".js-Pjax"],
  switches: {
    ".js-Pjax": Pjax.switches.sideBySide
  },
  switchesOptions: {
    ".js-Pjax": {
      classNames: {
        // class added to the old element being replaced, e.g. a fade out
        remove: "Animated Animated--reverse Animate--fast Animate--noDelay",
        // class added to the new element that is replacing the old one, e.g. a fade in
        add: "Animated",
        // class added on the element when navigating back
        backward: "Animate--slideInRight",
        // class added on the element when navigating forward (used for new page too)
        forward: "Animate--slideInLeft"
      },
      callbacks: {
        // to make a nice transition with 2 pages at the same time
        // we are playing with absolute positioning for the element we are removing
        // & we need live metrics to have something great
        // see associated CSS below
        removeElement: function(el) {
          el.style.marginLeft = "-" + (el.getBoundingClientRect().width/2) + "px"
        }
      }
    }
  }
})

请注意,remove包含Animated--reverse,这是一种简单的方法,不必进行重复的转换(slidein+reverse=>slideout)。

以下是一些与上述配置配合良好的CSS:

/*  注意:如果内容元素没有固定的宽度,则可能导致绝对定位时的问题 */
.js-Pjax { position: relative } /* 将进行切换的父元素 */
.js-Pjax-child { width: 100% }
/* 将要移除的元素的位置 */
.js-Pjax-remove {
  position: absolute;
  left: 50%;
  /* transform: translateX(-50%) */
  /* 转换无法使用,因为我们已经使用了移除效果的通用转换(例如animate.css)*/
  /* margin-left: -width/2; // made with js */
  /* 转换无法使用,因为我们已经使用了移除效果的通用转换(例如animate.css) */
}
/* CSS animations */
.Animated {
  animation-fill-mode: both;
  animation-duration: 1s;
}
.Animated--reverse { animation-direction: reverse }
.Animate--fast { animation-duration: .5s }
.Animate--noDelay { animation-delay: 0s !important;  }
.Animate--slideInRight { animation-name: Animation-slideInRight }
@keyframes Animation-slideInRight {
  0% {
    opacity: 0;
    transform: translateX(100rem);
  }
  100% {
    transform: translateX(0);
  }
}
.Animate--slideInLeft { animation-name: Animation-slideInLeft }
@keyframes Animation-slideInLeft {
  0% {
    opacity: 0;
    transform: translateX(-100rem);
  }
  100% {
    transform: translateX(0);
  }
}

为了给这个CSS提供上下文,这里有一个HTML片段:

<!doctype html>
<html>
<head>
  <title>Page Title</title>
</head>
<body>
  <section class="js-Pjax">
    <div class="js-Pjax-child">
      Your content here
    </div>
    <!--
    在替换过程中,您将看到以下tree::
    <div class="js-Pjax-child js-Pjax-remove Animate...">
      Your OLD content here
    </div>
    <div class="js-Pjax-child js-Pjax-add Animate...">
      Your NEW content here
    </div>
    -->
  </section>
  <script>...</script>
</body>
</html>

 history (Boolean, default: true)

启用 pushState() 的使用。禁用此选项将阻止PJAX更新浏览器历史记录。虽然可能,但在您希望这样做的地方几乎没有用处。

在内部,当 popstate 事件触发pjax(不再 pushState() )时使用此选项。

 analytics (Function | Boolean, default: 函数的推送  _gaq _trackPageview 或发送 ga pageview

允许您添加分析行为的函数。默认情况下,它尝试使用谷歌分析(如果页面上存在)跟踪页面视图。每次切换页面时都会调用它,即使是用于历史导航。

设置为false 可禁用此行为。

 scrollTo (Integer | [Integer, Integer] | False, default: 0)

当设置为整数时,这是切换页面时滚动到的值(以像素为单位)。

当设置为2个整数([x,y])的数组时,这是水平和垂直滚动的值。

将此设置为 false 以禁用滚动,这意味着页面将保持在加载新元素之前的相同位置。

 scrollRestoration (Boolean, default: true)

当设置为true时,PJAX将尝试在向后或向前导航时恢复滚动位置。

 cacheBust (Boolean, default: true)

当设置为true 时,PJAX将时间戳查询字符串段附加到请求的URL,以便跳过浏览器缓存。

 debug (Boolean, default: false)

启用详细模式。用于调试页面布局差异。

 currentUrlFullReload (Boolean, default: false)

当设置为true 时,单击指向当前URL的链接将触发整页重新加载。

当设置为false 时,单击这样的链接将导致PJAX在不重新加载整个页面的情况下加载当前页面。如果要添加一些自定义行为,请向链接中添加单击侦听器并调用 preventDefault() 。这将阻止PJAX接收事件。

注意:这必须在PJAX被实例化之前完成,否则将首先调用PJAX的事件处理程序,并且不会调用preventDefault()

以下是一些示例代码:

 var links = document.querySelectorAll(".js-Pjax");
  for (var i = 0; i < links.length; i++) {
    var el = links[i]
    el.addEventListener("click", function(e) {
      if (el.href === window.location.href.split("#")[0]) {
        e.preventDefault();
        console.log("Link to current page clicked");
        // 这里是自定义代码。
      }
    })
  }
  var pjax = new Pjax()

(请注意,如果cacheBust设置为true,则由于附加了查询字符串以强制执行cacheBust,因此检查href是否与当前页面的URL相同的代码将不起作用)。

 timeout (Integer, default: 0)

XHR请求的超时时间(毫秒)。设置为0以禁用超时。

# Events (事件)

无论如何调用PJAX,它都会触发许多事件。

所有事件都是从文档中激发的,而不是单击的链接。

  • pjax:send - 在PJAX请求开始后激发。

  • pjax:complete - 在PJAX请求完成后激发。

  • pjax:success - 在PJAX请求成功后激发。

  • pjax:error - 在请求失败后激活,请求对象将作为event.options.request 传递。

如果要实现加载指示器(例如:Topbar),则send complete 是一对很好的事件。

document.addEventListener('pjax:send', topbar.show)
document.addEventListener('pjax:complete', topbar.hide)

 # HTTP Headers (HTTP头处理)

PJAX在发出和接收HTTP请求时使用几个自定义头文件。如果请求将发送到您的服务器,您可以使用这些头来获取关于响应的一些元信息。

 Request Headers (请求报头)

PJAX随每个请求发送以下头文件:

  • X-Requested-With: "XMLHttpRequest"

  • X-PJAX: "true"

  • X-PJAX-Selectors:选择器的序列化JSON数组,取自options.selectors 。您可以使用它仅返回PJAX将用于切换的元素,而不是发送整个页面。请注意,您需要在服务器上对其进行反序列化(例如使用 JSON.parse() )

 Response Headers (响应头)

PJAX在响应上查找以下标题:

X-PJAX-URL  或  X-XHR-Redirected-To

PJAX首先检查XHR对象的 responseURL 属性,以查看请求是否由服务器重定向。大多数浏览器都支持这一点,但并非全部支持。为了确保PJAX能够知道请求何时被重定向,您可以在响应中包含这些头中的一个,设置为最终的URL。

# 准备就绪状态

大多数情况下,您将拥有与当前DOM相关的代码,这些代码仅在DOM就绪时执行。

由于PJAX不会在每次加载页面时自动重新执行以前的代码,因此需要添加代码以重新触发准备好DOM的代码。下面是一个简单的例子:

function whenDOMReady() {
  // do your stuff
}
whenDOMReady()
var pjax = new Pjax()
document.addEventListener("pjax:success", whenDOMReady)

注意:不要在 whenDOMReady 函数中创建pjax实例。

如果您只想更新一个特定的部分(这是一个好主意),您可以在函数中添加与DOM相关的代码,并在触发 pjax:success 事件时重新执行该函数。

// do your global stuff
//... DOM ready code
function whenContainerReady() {
  // do your container related stuff
}
whenContainerReady()
var pjax = new Pjax()
document.addEventListener("pjax:success", whenContainerReady)

# FAQ (常见问题解答)

问:Disqus已经不起作用了,我该怎么解决?

答:你需要做一些事情:

  • 将Disqus包装到将添加到 selector 属性的Dom元素中(或只使用 class="js-pjax" ),并确保每个页面上至少有一个空包装器(以避免页面之间的Dom差异)

  • 像下面那样编辑你的Disqus内容。

pjax集成前的Disqus内容

<script>
  var disqus_shortname = 'YOURSHORTNAME'
  var disqus_identifier = 'PAGEID'
  var disqus_url = 'PAGEURL'
  var disqus_script = 'embed.js'
  (function(d,s) {
  s = d.createElement('script');s.async=1;s.src = '//' + disqus_shortname + '.disqus.com/'+disqus_script;
  (d.getElementsByTagName('head')[0]).appendChild(s);
  })(document)
</script>

pjax集成后的Disqus内容

<div class="js-Pjax"><!-- 需要出现在每个PJAX化的页面上,即使是空的 -->
<!-- if (some condition) { // 最终的服务器端测试,以了解是否包含此脚本 -->
  <script>
    var disqus_shortname = 'YOURSHORTNAME'
    var disqus_identifier = 'PAGEID'
    var disqus_url = 'PAGEURL'
    var disqus_script = 'embed.js'
    // here we will only load the disqus <script> if not already loaded
    if (!window.DISQUS) {
      (function(d,s) {
      s = d.createElement('script');s.async=1;s.src = '//' + disqus_shortname + '.disqus.com/'+disqus_script;
      (d.getElementsByTagName('head')[0]).appendChild(s);
      })(document)
    }
    // 如果尚未加载,这里我们只加载Disqus<script>
    // see https://help.disqus.com/developer/using-disqus-on-ajax-sites
    else {
      DISQUS.reset({
        reload: true,
        config: function () {
          this.page.identifier = disqus_identifier
          this.page.url = disqus_url
        }
      })
    }
  </script>
<!-- } -->
</div>

注意:PJAX只处理要切换的容器的内联的<script>块。


文档原文:https://github.com/MoOx/pjax查看应用教程文章

英文不好,中文文档均为在线翻译,有误之处请指出。转载请著名出处《热腾网》。 给我发送邮件