Faster subsequent page-loads by prefetching in-viewport links during idle time.
简介
quicklink
是由Google开发的一个js库,它的作用是将资源预加载,让接下来的网页访问更加的顺畅。
How it works
Quicklink attempts to make navigations to subsequent pages load faster. It:
Quicklink试图使导航到后续页面加载更快
Detects links within the viewport (using Intersection Observer)
Waits until the browser is idle (using requestIdleCallback)
Checks if the user isn’t on a slow connection (using
navigator.connection.effectiveType
) or has data-saver enabled (usingnavigator.connection.saveData
)Prefetches URLs to the links (using
<link rel=prefetch>
or XHR). Provides some control over the request priority (can switch tofetch()
if supported).
上面这段话是quicklink在github中的介绍。它介绍了quicklink
的4个特性,下面逐个介绍这些特性。
1. Detects links within the viewport
通过Intersection Observer
检测视口内的链接。Intersection Observer API
提供了一种异步观察目标元素与顶级文档viewport的交集中的变化的方法。
使用方法:
const observer = new IntersectionObserver(callback, option?);
// eg: 用来监听页面内所有的a标签是否出现在视窗内
const observer = new IntersectionObserver(entries=>{
entries.forEach(entry=>{
const link = entry.target;
console.log(link)
})
});
Array.from(document.querySelectorAll("a"),link=>{
observer.observe(link);
})
复制代码
具体的使用方法请参考:Intersection Observer API – MDN
2. Waits until the browser is idle
等浏览器空闲的时候来做这些操作,不影响用户当前页面的使用体验。它是通过requestIdleCallback
来实现的。
**window.requestIdleCallback()
**方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。
var handle = window.requestIdleCallback(callback[, options])
复制代码
具体的使用方法请参考:requestIdleCallback – MDN
3. Checks if the user isn’t on a slow connection
检查用户是否在慢速连接上(使用 navigator.connection.effectiveType
获取当前网络类型) 或已启用数据保护程序(使用 navigator.connection.saveData
用户是否在用户代理上设置了减少的数据使用选项)。典型的用流量来换取用户的体验的优化。
具体的使用方法请参考:navigator.connection.effectiveType -MDN 与 NetworkInformation.saveData – MDN
4. Prefetches URLs to the links
将URL链接预加载。通过使用<link rel=prefetch>
或者XHR
。提供一些对请求优先级的控制(如果支持,可以切换到fetch
)。Resources Hints
包含如下内容:
- prefetch
- preconnect
- preload
- prerender
源码解读
quicklink的加载流程:
-
检测网页中的链接是否出现在视口中,等待链接出现在视口,执行步骤2。
-
等待浏览器空闲后执行3。
-
判断当前的网络连接是否是2G,如果是则停止执行,如果不是2G网络,执行步骤4。
-
预加载链接指向资源。
通过它的使用方法找到入口,点击查看具体使用方法。
入口函数
如果用户的有效连接类型和数据保护程序首选项表明这将是有用的,则预取一个URL数组。 默认情况下,查看“document”的in-viewport链接。还可以处理提供的DOM元素或静态URL数组。
源码路径:/src/index.mjs
/* 参数信息
* @param {Object} options - Configuration options for quicklink
* @param {Object} [options.el] - DOM element to prefetch in-viewport links of
* @param {Boolean} [options.priority] - Attempt higher priority fetch (low or high)
* @param {Array} [options.origins] - Allowed origins to prefetch (empty allows all)
* @param {Array|RegExp|Function} [options.ignores] - Custom filter(s) that run after origin checks
* @param {Number} [options.timeout] - Timeout after which prefetching will occur
* @param {Number} [options.throttle] - The concurrency limit for prefetching
* @param {Number} [options.limit] - The total number of prefetches to allow
* @param {Function} [options.timeoutFn] - Custom timeout function
* @param {Function} [options.onError] - Error handler for failed `prefetch` requests
*/
export function listen(options) {}
// 回调函数
timeoutFn(() => {
(options.el || document).querySelectorAll('a').forEach(link => {
if (!allowed.length || allowed.includes(link.hostname)) {
isIgnored(link, ignores) || observer.observe(link);
}
});
}, {
timeout: options.timeout || 2000
});
复制代码
首先进行的是初始化参数,通过Object.assign
函数合并默认配置和设置的配置。
检测link出现在视窗内
quicklink
通过observer.observe(link)
监视节点元素,observer
是IntersectionObserver
对象的实例,用来监听link出现在视窗内。如果符合条件,调用prefetch
方法,创建资源链接。
源码路径:/src/index.mjs
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.unobserve(entry = entry.target);
// Do not prefetch if will match/exceed limit
// 如果超过或者匹配限制,则不进行预加载
if (toPrefetch.size < limit) {
toAdd(() => {
prefetch(entry.href, options.priority).then(isDone).catch(err => {
isDone();
if (options.onError) options.onError(err);
});
});
}
}
});
});
复制代码
prefetch函数
使用可选的首选获取优先级预取给定的URL。返回一个Promise
对象,通过Promise.all
函数,加载多个link资源。
源码路径:/src/index.mjs
/** 参数详情
* @param {String} url - the URL to fetch
* @param {Boolean} [isPriority] - if is "high" priority
* @param {Object} [conn] - navigator.connection (internal)
* @return {Object} a Promise
*/
const toPrefetch = new Set();
export function prefetch(url, isPriority, conn) {
if (conn = navigator.connection) {
if (conn.saveData || /2g/.test(conn.effectiveType)) return;
}
return Promise.all(
[].concat(url).map(str => {
if (!toPrefetch.has(str)) {
toPrefetch.add(str);
// 降级处理进行资源预加载
return (isPriority ? priority : supported)(
new URL(str, location.href).toString()
);
}
})
);
}
复制代码
异步函数
如果浏览器支持requestIdleCallback,则使用原生的函数,如果不支持,则使用setTimeout函数做ployfill。
源码路径:/src/request-idle-callback.mjs
const requestIdleCallback = window.requestIdleCallback ||
function (cb) {
const start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
},
});
}, 1);
};
export default requestIdleCallback;
复制代码
资源请求
quicklink
预加载资源的三种策略
源码路径:/src/prefetch.mjs
1. prefetch
function viaDOM(url) {
return new Promise((res, rej, link) => {
link = document.createElement(`link`);
link.rel = `prefetch`;
link.href = url;
link.onload = res;
link.onerror = rej;
document.head.appendChild(link);
});
};
复制代码
2. XHR
function viaXHR(url) {
return new Promise((res, rej, req) => {
req = new XMLHttpRequest();
req.open(`GET`, url, req.withCredentials=true);
req.onload = () => {
(req.status === 200) ? res() : rej();
};
req.send();
});
}
复制代码
3. fetch
export function priority(url) {
return window.fetch ? fetch(url, {credentials: `include`}) : viaXHR(url);
}
复制代码
进行预加载策略的降级处理
// 判断是否支持prefetch
function hasPrefetch(link) {
link = document.createElement('link');
return link.relList && link.relList.supports && link.relList.supports('prefetch');
}
export const supported = hasPrefetch() ? viaDOM : viaXHR;
复制代码
总结
quicklink
是前端性能优化的一种手段,使用户后序页面的第一次访问加快。它检测页面的滚动,可以预加载出现在视窗内的页面链接,预加载下一页的资源,提高用户体验。是一种以用户网络流量来换取体验的手段,我们也可以结合网站的用户行为偏好,只预加载概率高的页面。同时,也通过它的源码熟悉前端加载资源的优化方式。PWA技术是优化用户的二次访问效率和体验。
写在最后
分享我私藏的TS教程,从0到高阶全系列,点击链接,0元获取www.yidengxuetang.com/pub-page/in…