博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
quicklink解析
阅读量:7104 次
发布时间:2019-06-28

本文共 4385 字,大约阅读时间需要 14 分钟。

简介

quicklink是一个js库,可以预加载出现在视口的网页链接,提高用户体验。它的加载过程如下:

1.检测网页中的链接是否出现在视口中,等待链接出现在视口,执行步骤2。
2.等待浏览器空闲后执行3。
3.判断当前的网络连接是否是2G,如果是则停止执行,如果不是2G网络,执行步骤4。
4.预加载链接指向资源。

使用方式

参考链接

quicklink源码解析

quicklink的入口函数接受传入的配置参数,通过Object.assign函数和默认的配置选项合并。接着执行timeoutFn异步方法,该方法接收一个回调函数,在回调中主要逻辑如下:

如果传入的options参数中有urls属性,则直接执行预加载,否则通过document.querySelectorAll方法获取所有a标签元素的NodeList,然后便利该元素节点列表,并监视该元素节点

observer.observe(link);

然后判断该a元素对象的href属性值所属的域名是否被允许访问,如果被允许访问,继续判断该链接是否应该被忽略,判断逻辑如下:

if (!allowed.length || allowed.includes(link.hostname)) {   // If there are any filters, the link must not match any of them   isIgnored(link, ignores) || toPrefetch.add(link.href);}

如果链接没有被忽略,则将该节点的href属性值加入到toPrefetch中

const toPrefetch = new Set();toPrefetch.add(link.href);

总的代码逻辑如下

export default function (options) {  options = Object.assign({    timeout: 2e3,    priority: false,    timeoutFn: requestIdleCallback,    el: document,  }, options);  observer.priority = options.priority;  const allowed = options.origins || [location.hostname];  const ignores = options.ignores || [];  options.timeoutFn(() => {    // If URLs are given, prefetch them.    if (options.urls) {      options.urls.forEach(prefetcher);    } else {      // If not, find all links and use IntersectionObserver.      Array.from(options.el.querySelectorAll('a'), link => {        observer.observe(link);        // If the anchor matches a permitted origin        // ~> A `[]` or `true` means everything is allowed        if (!allowed.length || allowed.includes(link.hostname)) {          // If there are any filters, the link must not match any of them          isIgnored(link, ignores) || toPrefetch.add(link.href);        }      });    }  }, {timeout: options.timeout});}

检测link出现在视口

上面通过observer.observe(link)监视节点元素,其中observer是IntersectionObserver对象的实例,被监听的节点对象出现在视口时,会执行new操作时传入的回调函数,并将出现在视口的节点对象通过数组的形式传给该回调。然后在回调中便利传入的数组,如果数组中的元素包含在toPrefetch对象中,则取消对该元素的监视,并对该a标签元素所对应的资源进行预加载。

const observer = new IntersectionObserver(entries => {  entries.forEach(entry => {    if (entry.isIntersecting) {      const link = entry.target;      if (toPrefetch.has(link.href)) {        observer.unobserve(link);        prefetcher(link.href);      }    }  });});

异步函数实现

如果浏览器支持requestIdleCallback,则使用原生的函数,如果不支持,则使用setTimeout函数做ployfill。

const requestIdleCallback = 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;

资源请求函数实现

预加载策略主要有三种

1.<link> prefetch

function linkPrefetchStrategy(url) {  return new Promise((resolve, reject) => {    const link = document.createElement(`link`);    link.rel = `prefetch`;    link.href = url;    link.onload = resolve;    link.onerror = reject;    document.head.appendChild(link);  });};

2.ajax加载

function xhrPrefetchStrategy(url) {  return new Promise((resolve, reject) => {    const req = new XMLHttpRequest();    req.open(`GET`, url, req.withCredentials=true);    req.onload = () => {      (req.status === 200) ? resolve() : reject();    };    req.send();  });}

3.Fetch请求加载

function highPriFetchStrategy(url) {  // TODO: Investigate using preload for high-priority  // fetches. May have to sniff file-extension to provide  // valid 'as' values. In the future, we may be able to  // use Priority Hints here.  //  // As of 2018, fetch() is high-priority in Chrome  // and medium-priority in Safari.  return self.fetch == null    ? xhrPrefetchStrategy(url)    : fetch(url, {credentials: `include`});}

网络类型判断

if (conn = navigator.connection) {    // Don't prefetch if the user is on 2G. or if Save-Data is enabled..    if ((conn.effectiveType || '').includes('2g') || conn.saveData) return;  }

将上面三种预加载方法封装成函数,暴露给外部使用

const supportedPrefetchStrategy = support('prefetch')  ? linkPrefetchStrategy  : xhrPrefetchStrategy;function prefetcher(url, isPriority, conn) {  if (preFetched[url]) {    return;  }  if (conn = navigator.connection) {    // Don't prefetch if the user is on 2G. or if Save-Data is enabled..    if ((conn.effectiveType || '').includes('2g') || conn.saveData) return;  }  // Wanna do something on catch()?  return (isPriority ? highPriFetchStrategy : supportedPrefetchStrategy)(url).then(() => {    preFetched[url] = true;  });};export default prefetcher;

转载地址:http://lzuhl.baihongyu.com/

你可能感兴趣的文章
函数指针
查看>>
MongoDB聚合运算之mapReduce函数的使用(11)
查看>>
Java -- 获取MAC地址
查看>>
URL中的#
查看>>
Sql Server导入Access数据库报不可识别的数据库格式 Microsoft JET Database Engine
查看>>
目标检測的图像特征提取之(一)HOG特征
查看>>
ArcGIS API for Silverlight 当DataGrid选中项时,地图聚焦弹出窗口,并可以播放音频文件...
查看>>
心情不好,用HTML5做个时钟
查看>>
5、Java的Timer定时器
查看>>
Java面试基础篇——第二篇:String类能为什么不能被继承?
查看>>
Flash Builder4安装SVN插件
查看>>
log4j输出多个自定义日志文件
查看>>
Jfinal 3.0 Model el表达式不能正常解析显示
查看>>
python接口自动化小工具(简版)
查看>>
Silverlight+WCF 新手实例 象棋 登陆与转向(十一)
查看>>
Elasticsearch 多字段聚合 结果封装为map
查看>>
apache 的 mod status 模块可能会泄漏服务器信息
查看>>
SpringBoot学习 (二) Spring Boot Security
查看>>
Nginx 的开发公司C 轮融资4300 万美元
查看>>
JVM:查看java内存情况命令
查看>>