本文正在参加「金石计划」

Prefetch 是一个谎言

我们知道,现在的应用程序已经发展到可以拆分为多个 JavaScript包了,为了获得更好的用户体验,这些 bundle 包通常需要预获取,即 prefetch! 但是现在的prefetch 效果有多糟糕我想你也知道。

例如,为了让应用程序拆解成多个包,在你的代码的某些地方,你会进行动态导入,比如 import('./some-dependency.js')。但是作为开发人员,你或许也会在代码片段中插入额外的动态导入。下面这段代码中,假设你将代码放在惰性加载块后面的 Buy 按钮后面,看起来像这样:

export default () => {return (<div><button onClick={async () => {// 惰性加载点击“购买”按钮背后的逻辑。(await import('./buy.js')).default();}}>Buy</button></div>);
};

但是这样做,现在你就会有一个新的问题要解决!当用户单击 Buy 按钮时,浏览器会惰性加载 buy.js 包。根据 bundle 的大小和网络的速度,这可能会引入一个显著的、明显的延迟。那我们能做些什么来改善呢?

Prefetch

幸运的是,浏览器自带 Prefetch 支持!所以你会把这样的东西放到头部部分。这个时候,你可能认为你已经解决上面提到问题。

<head><link rel="prefetch" href="buy.js"/><!-- 或选择一个可替代的策略<link rel="preload" href="buy.js"/><link rel="modulepreload" href="buy.js"/>-->
</head>

但是这段代码真的会如你所愿吗? 你也不太确定,因此接着,你在 Chrome 中测试它,发现一切都能正常工作。

但是很快,你就会得到反馈,在许多情况下,用户必须等待 Buy 按钮执行其操作。这种额外的等待正是损害用户体验的底线。那 prefetch 为什么不能如你所愿呢? 原因主要由几点:

  1. Modulepreload 不能在大多数浏览器中使用。
  2. Firefox 有 network.dns.disablePrefetchFromHTTPS 选项,默认设置为 true。默认情况下,Firefox 不会在 HTTPS 上 prefetch 任何内容。考虑到现在大多数东西都是 HTTPS,这有效地禁用了Firefox上的预取。
  3. 一些移动浏览器忽略 prefetch,因为移动浏览器认为这是在移动网络上,并试图节省带宽。

在闲置的时候加载

因此,大多数浏览器只在 network 空闲时才处理 prefetch。这是有意义的,但是为了让应用程序具有交互性,需要确保在出现次要事物(如高分辨率图像)之前出现交互性。如果等到页面上的所有东西都加载后才开始获取JavaScript,通常为时已晚。

你可以想象一个网站向你展示照片的时候。如果照片很大,需要下载一段时间。但你想在所有照片下载之前就开始与网站互动。但在所有图像都被解析之后再来获取 JavaScript 可能不是你想要的,因为这样会耗费你的耐心。

这个问题实际上说明了缺乏对浏览器中“何时”解析 Prefetch 的控制。

重复加载

prefetch 本应改善交互性,但在某些情况下,它可能会恶化交互性。

例如下面这段代码:

// 1. prefetch 开始
<link rel="prefetch" href="buy.js"/>// 2. 用户交互需要执行code,但是 prefetch 没有生效,于是重新 prefetch
<button onClick={async () => {// 惰性加载点击“购买”按钮背后的逻辑。(await import('./buy.js')).default();
}}>Buy</button>

想象一下如果你的连接速度很慢的时候,开始 Prefetch 并开始下载JavaScript。在JavaScript完全下载之前,用户与应用程序进行交互。现在,import('./buy.js') 被执行,但是 buy.js 不在缓存中。正在运行的 buy.js 请求尚未完成。但是由于请求是不完整的,浏览器不知道缓存头是什么,所以它不知道重用请求是否安全。所以浏览器做了安全的事情,发出另一个 buy.js 资源请求。现在,对同一资源的两个请求正在运行。

更糟糕的是,原始资源被解析,buy.js 被插入到浏览器缓存中,但是资源的解析并没有解除用户交互的阻塞。相反,UI 必须等待第二个 buy.js 返回,然后才能解除阻塞。

因此,prefetch 在某些情况下,可能导致多次请求相同的资源。

来自 Console 的警告

最后,如果某些浏览器检测到给定的预取资源在 x 秒内未被使用,则会发出控制台警告。因为浏览器会认为,你没有使用到它,就不应该预取它。

该怎么做

我觉得真正的 prefetch 是一种提示,告诉浏览器你将需要一些东西,因此浏览器应该在下载时有一个良好的开端,但我们想要的是用 用户可能需要的交互代码预填充缓存。

理想情况下,我们希望控制缓存,以便:

  • 控制何时填充缓存。
  • 理解 chunk 的依赖关系图,这样我们也可以 prefetch 合成的 bundle。
  • 控制请求,以便在请求尚未在 bundle 中时可以解除请求。

简单而言,我们希望从一个被动的执行预取转变为一个主动控制预取。

事实证明,service worker 能做到。Service worker 可以拦截请求并控制缓存中的内容。使用 service worker,我们可以对流程进行正确的控制,还可以了解 chunk 依赖关系图,并可以加载相关代码。

但是创建这样一个service worker并不容易,所以大多数开发人员都不这么做。

结论

你或许经常看到是“专家”给出的常见的性能优化建议中包含了 prefetch,以确保惰性加载的块不会对用户交互造成延迟。但事实证明,现实从来没有这么简单,使用 prefetch 在实践中并不像你所希望的那样有效。

相反,我们建议使用 service worker 来完全控制 prefetch 过程。这个结果对我们来说非常有用,因为它允许我们消除由于延迟加载代码而导致的交互延迟。

瞒不住了,Prefetch 就是一个大谎言相关推荐

  1. 为什么说人工智能是一个大谎言

    全世界只有3.14 % 的人关注了 数据与算法之美 人工智能是一个很大的谎言. 或者往轻了说,它是一个混淆概念.往重了说,是用来欺骗大众的流行语,并且流行度非常高. 其实真正的内涵是"机器学 ...

  2. 揭穿关于云计算的九大谎言

    本文讲的是揭穿关于云计算的九大谎言,[IT168 资讯]厂商的炒作,加上IT人的自欺,很快就会让人们对某种技术感到失望.如果你正在考虑云计算战略的话,请千万别被这些虚伪的承诺所迷惑. 如今,无论你去哪 ...

  3. 下一个大计算平台? Amazon Echo 研发幕后全揭露

    作为亚马逊打入智能家居并占据入口地位的标志产品,Echo的研发历程一波三折.本文记者采访内部一线人员,得到Echo问世幕后消息:亚马逊最初瞄准AR,不成后转做声控扬声器,Fire Phone的失败让这 ...

  4. 李彦宏乌镇谈人工智能:百度会很快和雄安宣布一个大的智能城市计划

    来源:亿欧 概要:百度董事长兼CEO李彦宏谈到了人工智能给C端.B端以及供给侧等方面带来的影响和变革. 12月4日,2017世界互联网大会在乌镇如火如荼地进行,在主题为"人工智能:让生活更美 ...

  5. 实验三:实现一个大素数生成算法

    一.实验内容 掌进一步掌握大素数分解的一般原理和实现方法.能用间接方法实现大素数分解.用代码实现Solovay-Strassen素性测试法或Miller-Rabin素性测试法. 二.分实现一个大素数生 ...

  6. Facebook图片存储系统Haystack——存小文件,本质上是将多个小文件合并为一个大文件来降低io次数,meta data里存偏移量...

    转自:http://yanyiwu.com/work/2015/01/04/Haystack.html 一篇14页的论文Facebook-Haystack, 看完之后我的印象里就四句话: 因为[传统文 ...

  7. java实现把一个大文件切割成N个固定大小的文件

    这个好像是我一年前去面试时的一道面试题,分享一下!考 java I/O 的! //java实现把一个大文件切割成N个固定大小的文件 package com.johnny.test; import ja ...

  8. C++20 - 下一个大版本功能确定

    C++20的功能特性已经于3月份冻结,显然这次终于来了一波大的改进,而不再是像之前C++14/C++17那般小打小闹的做小步快跑,尤其是三个讨论很久的大feature终于被合入主干:并且这些featu ...

  9. 用Python实现一个大数据搜索引擎

    用Python实现一个大数据搜索引擎 搜索是大数据领域里常见的需求.Splunk和ELK分别是该领域在非开源和开源领域里的领导者.本文利用很少的Python代码实现了一个基本的数据搜索功能,试图让大家 ...

最新文章

  1. 在WPF中实现平滑滚动
  2. Ticker View
  3. 什么从什么写短句_从什么到从什么造句
  4. Eclipse插件开发中对于外部Jar包和类文件引用的处理(彻底解决插件开发中的NoClassDefFoundError问题)...
  5. left join on、where后面的条件的区别
  6. stm32F10x 看程序知识点记录
  7. LeetCode 85.最大矩形
  8. 从oracle到mysql,主从到分库,一个普通项目数据库架构的变迁
  9. Ubuntu 挂载windows硬盘突然变成只读了???
  10. windows防火墙ntp服务器_Windows Server 2008 R2 NTP服务器
  11. 删除:大数据取舍之道 - 电子书下载(高清版PDF格式+EPUB格式)
  12. 【论文笔记】 Synthesizing Robust Adversarial Examples
  13. 安卓手机做服务器(django),完成废物利用
  14. 社会工程学(安全牛)
  15. [android] 运行编译报错:Entry name ‘assets/Icoxx.png‘ collided
  16. OSI七层参考模型详解
  17. Gridea+Coding Page个人网站搭建
  18. Android魅族等手机下拉刷新有留白(下拉悬停)
  19. 渡一教育公开课web前端开发JavaScript精英课学习笔记(二)变量类型,运算符
  20. python cv2什么意思_cv2.solvePnP方法标志的Python常量是什么?

热门文章

  1. 实验6、灰盒测试实验
  2. 如何批量检测查询域名或者网址是否被微信屏蔽拦截
  3. Xilinx ZYNQ开发板资料共享
  4. APP软件开发定制返利网分销商城APP
  5. 锐捷Wlan——AC热备实验(DHCP在核心交换机)
  6. CPU的三种工作模式:实模式、保护模式、长模式
  7. sci论文图排版技巧分享
  8. 论人工智能与软件工程的关系
  9. 利用CSS制作通栏,css6——通栏平均分布
  10. Cesium加载各种互联网地图(一)