Needlessly Promiscuous, Modularize!

npm

Need private packages and team management tools?Check out npm Orgs. »

@xfe-team/better-quicklink

0.0.2 • Public • Published

@xfe-team/better-quicklink

可以在空闲时间预获取页面可视区域(以下简称视区)内的链接,加快后续加载速度。

Fork 改进

当前工具库通过 fork 的方式进行了以下问题的改进:

  1. quicklink 参数 origins 应该可以使用正则数组或字符串用于匹配, 因为这样在大多数情况下更为实用
  2. quicklink 当前仅仅只是用过 html 的 tag 标签进行静态的 html 拉去. 然而, 我们可以使用 iframe 进行预拉去整个 html 包括 html 所关联的内部资源.

工作原理

Quicklink 通过以下方式加快后续页面的加载速度:

  • 检测视区中的链接(使用 Intersection Observer)。
  • 等待浏览器空闲(使用 requestIdleCallback)。
  • 确认用户并未处于慢速连接(使用 navigator.connection.effectiveType)或启用省流模式(使用 navigator.connection.saveData)。
  • 预获取视区内的 URL(使用 <link rel=prefetch> 或 XHR)。可根据请求优先级进行控制(若支持 fetch() 可进行切换)。

开发原因

该项目旨在为网站提供一套解决方案,预获取处于用户视区中的链接,同时保持极小的体积(minifiy/gzip 后 <1KB)。

安装方法

nodenpm 用户:

npm install --save @xfe-team/better-quicklink

用法

初始化后,quicklink 将自动在闲时预获取视区内的链接 URL。

快速上手:

<!-- 从 dist 目录下引入 quicklink -->
<script src="//zhcdn01.xoyo.com/xassets/lib/better-quicklink/{VERSION}/better-quicklink.umd.js"></script>
<!-- 初始化(你可以随时进行) -->
<script>
if(!!(window.IntersectionObserver && Array.prototype.includes && window.Promise && window.URL)) {
  quicklink();
}
</script> 

举个例子,你可以在 load 方法触发之后进行初始化:

<script>
window.addEventListener('load', () =>{
   quicklink();
});
</script> 

或者导入 ES 模块:

import quicklink from "dist/better-quicklink.mjs";
quicklink();

以上方法适用于多页网站。单页应用可以搭配 router 使用 quicklink:

  • 进入新路由地址后,调用 quicklink()
  • 针对特定 DOM 元素/组件调用 quicklink()
  • 调用 quicklink({urls:[...]}),传入自定义 URL 集合进行预获取。

API

quicklink 接受带有以下参数的 option 对象(可选):

  • el:指定需要预获取的 DOM 元素视区。
  • urls:预获取的静态 URL 数组(若此参数非空,则不会检测视区中 document 或 DOM 元素的链接)。
  • timeout:整型数,为 requestIdleCallback 设置超时。浏览器必须在此之前进行预获取(以毫秒为单位), 默认取 2 秒。
  • timeoutFn:指定超时处理函数。默认为 requestIdleCallback。也可以替换为 networkIdleCallback(详见 demo)等自定义函数。
  • priority:布尔值,指定 fetch 的优先级。默认为 false。若配置为 true 将会尝试使用 fetch() API(而非 rel=prefetch)。
  • origins: 静态字符串数组,包含允许进行预获取操作的 URL 主机名。默认为同域请求源,可阻止跨域请求。
  • ignores: RegExp(正则表达式),Function(函数)或者 Array(数组),用于进一步确定某 URL 是否可被预获取。会在匹配请求源之后执行。

待探索:

Polyfills

quicklink:

  • requestIdleCallback 的一个非常小的回退。
  • 需要支持 IntersectionObserver (请参阅 CanIUse)。我们推荐使用 Polyfill.io 等服务选择性地实现此功能:
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>

或者,请参见 Intersection Observer polyfill

方法

为预获取操作自定义超时时间

默认超时时间为 2 秒(通过 requestIdleCallback),这里我们重写为 4 秒:

quicklink({
  timeout: 4000
});

设置用于检测链接的 DOM 元素

默认值为 document

const elem = document.getElementById('carousel');
quicklink({
  el: elem
});

自定义预获取 URL 数组

如果你想指定用于预获取的静态 URL 列表,而不是视区内的链接,你可以使用自定义 URL。

quicklink({
   urls: ['2.html','3.html', '4.js']
});

为预获取设置请求优先级

默认为低优先级(rel=prefetch 或 XHR)。对于高优先级(priority: true)的操作,尝试使用 fetch() 或退阶使用 XHR。

quicklink({ priority: true });

自定义受允许请求源列表

指定可被预获取的主机名列表,默认情况下仅允许同源主机名。

划重点:你还得加上自己的主机名!

quicklink({
  origins: [
    // 添加我自己的
    'my-website.com',
    'api.my-website.com',
    // 添加第三方的
    'other-website.com',
    'example.com',
    // ...
  ]
});

允许所有源

允许所有跨域请求。

注意:可能会导致 CORB 以及 CORS 问题!

quicklink({
  origins: true,
  // 或者
  origins: []
});

定义一个自定义正则匹配所有源列表(Fork 变更)

指定可被预获取的正则主机名列表,默认情况下仅允许同源主机名。

quicklink({
  origins: [
    /xoyo\.com$/i,
    /baidu\.com$/i
  ]
});

使用 iframe 进行预加载 (Fork 变更)

使用属性 useIframeStrategy 用于启用 iframe 预加载

window.quicklink({
  useIframeStrategy: true,
  origins: [
     /xoyo\.com$/i,
     /baidu\.com$/i
  ],
});

自定义忽略模式

以下过滤器会在匹配 origin 之后执行。Ignores 在避免大型文件下载或 DOM 属性动态响应时十分有用!

// 默认启用同源限制。
//
// 这个示例会忽略所有对以下模式的请求:
//  - 所有 "/api/*" 路径名
//  - 所有 ".zip" 扩展名
//  - 所有带有 noprefetch 扩展名的 <a> 标签
//
quicklink({
  ignores: [
    /\/api\/?/,
    uri => uri.includes('.zip'),
    (uri, elem) => elem.hasAttribute('noprefetch')
  ]
});

也许你还想忽略那些包含 URL fragment 的 URL(比如 index.html#top),不对它们进行预获取。那么对于在页面中使用了锚点标题,或者在单页应用设置了 URL fragment 的情况,这个功能可以避免对类似的 URL 进行预获取。

使用 ignores 来实现:

quicklink({
    ignores: [
        uri => uri.includes('#')
        // 或者使用正则表达式: /#(.+)/
        // 或者使用元素匹配: (uri, elem) => !!elem.hash
    ]
});

浏览器支持

quicklink 提供的预获取是渐进增强的,跨浏览器支持如下:

  • 不使用 polyfills:Chrome,Firefox,Edge,Opera,Android Browser,Samsung Internet 支持。
  • 使用 Intersection Observer polyfill(gzipped/minified 后大约 6KB):Safari,IE9+ 支持。

部分功能支持分层实现:

直接使用预获取器

quicklink 包含一个预获取器,可以单独导入其他项目中。方法是先将 quicklink 作为依赖项安装,然后按如下方式使用:

<script type="module">
import prefetch from '../src/prefetch.mjs';
 
const urls = ['1.html', '2.html'];
const promises = urls.map(url => prefetch(url));
Promise.all(promises);
</script> 

Demo

这个 WebPageTest demo 演示了 quicklink 的预获取功能,它将页面加载性能提高了 4 秒! 这个 Youtube 视频 对使用预获取之前和之后进行了对比。

为了做演示,我们在 Firebase 上部署了一个 Google Blog,接着部署了另一个在主页添加了 quicklink 的版本,测试从主页导航到一个自动预获取的文章所用时间。结果表明预获取版本加载速度更快。

请注意:这绝不是对这项技术优缺点的详尽测试,只是演示了该方法可能带来的潜在改进。你自己的实现可能不尽相同。

相关项目

  • 在用 Gatsby 吗? 现在可以免费下载它了。它使用 Intersection Observer 预获取视图中的所有链接,本项目灵感亦来源于此。
  • 想要更加数据驱动的方案吗? 参见 Guess.js。它根据用户上网方式,使用数据分析和机器学习来预获取资源。它还有 WebpackGatsby 的插件。

ChangeLog

0.0.1 (2019-10-16)

  • feat: 临时修复 prefetch 潜在可能异常的场景

0.0.1 (2019-10-08)

  • feat: init commit

许可证

本项目已获得 Apache-2.0 许可。
原作者: addyosmani addyosmani@gmail.com
Fork 来源: quicklink

install

npm i @xfe-team/better-quicklink

Downloadsweekly downloads

3

version

0.0.2

license

Apache-2.0

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar
  • avatar
Report a vulnerability