BFCache或There and back。Yandex报告

人们经常在浏览器中使用返回按钮到上一页,这也许比您想象的要频繁。如果是这样,那为什么要立即将页面从浏览器的内存中扔出,并花一秒钟的时间和流量重新打开它呢?为了使用户可以快速返回,发明了BFCache技术,这在开发接口时必须记住。维克多·霍姆亚科夫(Victor Khomyakov)维克托·霍米亚科夫 找出“往返缓存”,并使用不同的API编译了BFCache兼容性表。


-您好,我叫维克多。我是一个处理搜索页面的庞大团队的成员。



至少,您已经在Google上看到了相同或相似的页面。尤其是,我要处理加载该页面的速度问题-使其尽快在服务器上呈现,并尽快下载并显示给客户端。它为什么如此重要?客户等待您的页面加载的次数越少,他就不会等待离开您的可能性就越小。客户越有可能成功地转换成其他东西,净促销分数就越高。也就是说,客户会愉快地告诉每个人,他们知道这是一个很棒的很棒的页面-它加载速度非常快,使用起来非常方便。最终,您可以获得更多的钱。或您的公司,那么它将给您奖励。

我将举一些知名公司的例子。 Google进行了一项实验。他们有意在搜索页面上嵌入了一个延迟,并测量了延迟如何影响性能。事实证明,平均每个用户的搜索量减少了0.5%。什么是百分之五?计算:亿万的Google用户中有百分之五的人是一个很大的数字。

bing做了同样的实验。他们不相信Google,因此决定仔细检查。他们得到了类似的结果:页面放慢时,每位用户的收入明显减少。为什么减速?因为将页面的速度减慢精确的毫秒数要比将页面加速相同的速度要容易得多,以便可以在生产中轻松复制页面。

来自速卖通的示例。他们将网站速度提高了36%,并收到了来自用户的大量订单。订单是直接付款。总的来说,每个人都已经清楚速度非常重要,它会通过一定数量的指标来影响所赚的钱。

还有一个因素。今天,我们已经讨论了图像优化。通过优化流量,减少下载数量,您可以减少向主机托管商支付外发流量的费用。这也是剩下的钱。如果我突然从任何主机为您提供10%的流量折扣而没有任何限制和条件怎么办?并且,如果我建议确保您的网页份额(例如百分之十)几乎由用户立即加载?没有人会拒绝。

我今天将要讨论的技术是可能的解决方案之一,它对使用哪种堆栈,使用哪种技术没有什么限制,但同时有望为您带来如此巨大的收益。

首先,Google会收集有关这些浏览器在其浏览器中的使用情况的统计信息。他们发布了这样一个数字:事实证明,平均而言,在移动Chrome浏览器中所有页面的打开次数和所有导航中,大约有19%是整个故事的来回移动。觉得这意味着什么?如果四舍五入,事实证明有20%的导航将移动到用户刚浏览过的页面。

对于我们来说,对于页面的作者而言,这意味着:即使用户离开了页面,他也有很大的机会要回来。一方面,这可能恰恰是手机的问题:一切都很小,手指容易碰到,单击链接离开页面,然后说:“哦,天哪!我要回来”。但是在台式机上,情况大致相同:数量较少,但回报率仍然很高。

我们目前在做什么?我们在用户时间和流量上的花费很低。也就是说,我们开始重新加载同一页面,对其进行解析,重新创建DOM,在屏幕上重新绘制所有内容,加载并执行JavaScript。

浏览器是非常强大的功能。他正在尝试尽可能使用缓存。而且大部分资源可能都在他的缓存中。他不会等待网络中的消息,而是直接从缓存中提取消息。例如,在V8引擎中,还解析了JavaScript的解析结果。也就是说,浏览器将不会重新加载和解析JS,并且在大多数情况下,它将需要立即执行它。但是,仍然需要重建DOM,重新加载非缓存资源以及运行JS。

解决方案表明了自己。我们在做什么?当用户离开我们的页面时,我们不会立即对其进行清理。我们只需保存其状态,并在视觉上将其隐藏给用户,以便在引擎盖下将其保留给浏览器使用。

如果用户决定退货,我们该怎么办?只需向他显示相同的保存页面即可。它几乎可以立即显示。



这项技术从“来回”一词称为后退/前进缓存。bfcache的缩写。

这是一个示例,说明打开和关闭bfcache时相同的浏览器,相同的程序集的行为。无论在何处,首页的打开都同样缓慢。但是更进一步,当我们开始在故事中来回移动时,左侧会出现明显的停顿,而右侧则不会出现。在左侧,历史上的常规移动需要花费大量时间。在右边,一切发生得非常非常快。

显示GIF

我们搜索中的类似示例。左侧是禁用了bfcache的macOS上的常规Safari,右侧是具有默认设置并启用bfcache的同一Safari。这是一个相当普遍的情况:一个人进入搜索,可能不确切知道他在寻找什么,可能会问几个查询选项。我问了第一个请求-事情不对。第二个请求似乎更好。第三-不,更糟的是,返回上一个请求。此时此刻,最好不要让他等待。他刚刚看到了这个先前的请求,马上就显示出来。

或第二种选择,如果您有分页和关于此问题的几页内容。男子翻阅问题。我转到第二,第三,第四页,看了-不,有什么问题,我会回来的。而且,我们可以再次将近乎先前的页面显示给他。

一个重要的问题是安全性。当用户不在其上的页面处于隐藏状态时,它可以访问各种API,这些API可让您读取手机或计算机的硬件状态。以下是立即想到的内容的简短列表:地理位置,更改设备在空间中的位置,使用摄像头以及麦克风发出的声音。

然后,当页面出现时,重要的是它不能访问隐藏期间发生的所有事件。否则,将打开一个附加通道来监视用户。重要的是,不要让她一直都在这段时间内获得您的运动历史以及麦克风和相机的录音。浏览器开发人员也不应忘记这一点。

API和浏览器支持


更接近主题。假设我已经说服了您,您就是:“是的,一个好话题,我们必须为此努力。”我们可以使用哪些API,如果同意将bfcache考虑在内,可以使用哪些API,浏览器如何支持?

bfcache已经存在于哪里,在哪里可以看到?

-长期以来,它已在Firefox,Safari(以及macOS和iOS)浏览器以及Internet Explorer 11(!)中实现。通常,我们责骂Microsoft开发人员,但在这里他们只是尝试过。

-在适用于Android的浏览器UC Browser 11/12中实现。

-突然他不在Chrome中。在Chromium中,此功能正在开发中。



因此,当他们在Chromium中执行此操作时,几乎所有这些浏览器(并且这不是完整列表)都会或早或晚获得此功能-免费,无需SMS和注册。

是否有任何一种API?我想管理bfcache,我想直接从JavaScript中打开和关闭它,以查明bfcache中是否有任何页面。这样的API怎么样?没有这样的API。这是有意识地完成的:该页面不应该为每个人和自己打开或关闭bfcache。或找出此bfcache中是否有人。这全是出于安全考虑。


幻灯片链接

但是我们有什么呢?导航类型。当我们要预先渲染页面时,有一种链接类型-链接预渲染。他有一种特殊的导航类型:该页面将以NavigationType“ prerender”打开。如果我们只是在浏览器中打开页面,则将进行“导航”。如果我们单击“更新”按钮,它将被“重新加载”。

我们对这里的“ back_forward”导航类型很感兴趣,这清楚地表明用户在故事中来回移动。这正是bfcache可以使用的导航类型。



另一个API是pageshow pagehide事件。它们已经存在于浏览器中。因此,当您的页面在浏览器中显示给用户时,将触发页面显示;当页面由于某种原因被隐藏时,将触发页面隐藏。坚持不懈的领域为他们提供了补充。如果页面是隐藏的,并且同时将其放置在bfcache中,则持久字段将为true。如果从bfcache提升页面时显示该页面,则持久字段将再次为true。

因此,如果浏览器不打算缓存页面,则pageh将持续为false。而且,如果浏览器在正常加载期间显示该页面,或者它不使用bfcache,则pageshow也将持续显示为false。


幻灯片链接

事件支持在几乎所有浏览器中都可用,即使那些尚不支持bfcache的浏览器也是如此。


幻灯片中

链接持久字段也是如此。它已经存在于Chrome中,并且Chrome仍然不支持bfcache。也就是说,该字段将始终存在于其中,但现在它将是错误的。

当我遇到bfcache这种现象时,我必须对其进行研究,四面八方,观察其工作原理。当我打开处理程序中的persistent字段的值等于什么时,我立即想在页面上看到。



似乎一切都很简单。我写了一个处理程序,然后输出到console.log()。但是在某些浏览器中打开DevTools时,bfcache可能突然关闭。也就是说,我打开了DevTools,在页面之间来回浏览,并且我坚持的始终是false,页面不会进入bfcache。好的,我还有另一个强大的工具-警报。

但不是。现代浏览器在以pagehide方式卸载页面时,beforeunload和unload处理程序只是忽略警报,它根本无法在其中工作。再说一次,我没有看到我想要的。



好吧,我还有一个杀手级产品。我正处于自己正在浏览的页面的一个块中,只需添加事件内容的文本即可记录所有内容。此方法有效。



请使用所有东西。我调试了代码,它对我有用,我可以继续进行下去。当然,我不会忘记,毕竟,外部静态脚本更适合使用,以免在页面上加载相同的内联代码,而是使用文件缓存。

我将调试后的代码放入外部脚本中。



但是,不,在Safari中,页面显示的页面隐藏处理程序掉线了!由于某些原因,它们无法通过外部脚本运行。

好的,我已经有一个工作版本。我不得不这样离开。



我将简要列出我在短短一天内要努力完成的工作。首先,DevTools可以禁用缓存。您可能还记得,Chrome的“网络”标签上的DevTools中有一个复选框“禁用缓存”。它禁用网络缓存,它可能不会影响bfcache,或者可能会影响。这样的类比很清楚:我们打开了DevTools,这意味着我们正在开发并且可能不需要缓存。也许它困扰我们。



第二个功能是警报。 Firefox和Safari将默默地忽略它,并继续执行处理程序,就像没有警报一样。控制台中只有一个旧版本的Chrome会以红色显示错误-您有一个警报,我阻止了它,知道了!

再次提醒您,可能不会调用Safari中外部脚本的处理程序,这很奇怪。

还有一个重要的新闻。如果您的页面被缓存,即您收到了一个pagehide事件,并且说持续存在,并且浏览器对您说:“是的,我将其放入了缓存”-这不能保证该页面以后会被使用来自此缓存的数据将被显示并显示给用户。因为如果内存不足,浏览器可能会决定清除此缓存。因为用户可以关闭浏览器并且不能在任何地方导航。记住这一点。

实施功能


我开始进一步研究文档,以研究如何利用这些知识。出乎意料的是,文档是。也就是说,您可以在Internet上找到bfcache如何在浏览器中工作的描述。但是,我读得越多,不同浏览器之间积累的差异就越大。

在一个方面,它像这样工作,在另一个方面,它工作。在一个方面,一个干扰,另一个不干扰。当将页面放置在bfcache中时,开发人员不知道如何正确处理许多API。他们说:好的,如果页面使用此API,那么我只是忽略它,在任何情况下都不要将其放在缓存中。并且此列表在不同的浏览器中有所不同,每种浏览器都适合它。

然后我开始将自己学到的东西合并到一张桌子中。我得到类似以下内容:



我阅读了有关浏览器的文档-Firefox,Safari,Chromium系列。尽管已过时,但有关于IE的可用文档。我们程序员不喜欢在浏览器更改后更新文档吗?当我意识到信息已经过时时,我开始在浏览器中测试我的小页面,并检查哪些API有效,哪些无效。

这还不够:我不知道原则上看哪个API,但根本不对所有API进行分类?而且我不得不研究浏览器引擎本身的源代码,也就是说,该代码原来是最准确和可靠的知识源。目前,此板块(在您面前是它的一部分,这里是完整版本的链接)是有关哪些API允许或禁止bfcache在浏览器中运行的知识的最全面的集合。

不会干扰选中标记和绿色的API,那些绝对会阻止页面进入bfcache的API将标记为红色。白色字段是在任何地方都没有描述的空格。

火狐浏览器


以下是来自特定浏览器的一些有趣的详细信息。我将从Firefox开始,他是第一个这样做的人。


幻灯片中

链接我从Firefox来源中学到的最重要的事情是,使用bfcache时,它可以将无法将页面放入缓存的所有原因写入磁盘上的文本日志。


幻灯片中的链接

而且,我什至设法找到了如何做到这一点的方法。有两个秘密的环境变量:第一个变量指示要记录的内容,第二个变量指示要写入日志的文件。之后,我们启动二进制文件,瞧!我们将在上一张幻灯片中大致看到,“由于某种原因(由于不同的原因而无法缓存此类html”)形式的行。我们可以立即阅读它,非常方便。



如果您想尝试一次,则可以在Firefox中打开about:联网页面。您可以在“日记帐”部分中输入相同的字段。我们可以在两个字段中指示要记录的内容和位置,并使用按钮启动和停止日志。


幻灯片中的链接

Firefox何时拒绝缓存页面?如果您有不完整的请求,包括AJAX请求或对资源(例如图像)的请求,那么他将拒绝将页面放入bfcache。他认为它尚未完成,尚未完成下载,存在某种可疑活动。但是,所有这些都不适用于收藏夹图标。他认为,如果您忘记了网站图标,则该图标无法加载-好的,他会这样做,这对您的网站来说是正常的。

如果您的脚本运行时间很长,浏览器会询问:由于花费时间,因此会阻塞UI,或者击败它吗?而且,如果您同意,则认为这样的页面有误,我们不会对其进行缓存。

如果您使用IndexedDB ...这是一个有启发性的故事。以前,在第一个实现中,它们看起来是:如果您拥有IndexedDB并且有一个不完整的事务,则不会缓存这样的页面,因为尚不清楚如何使用它(我们试图将其隐藏在事务中间)。但是随后,他们在重构期间只是丢失了这段代码。就像您想象的那样,他们没有对此进行测试。他们甚至在bugtracker中有一个bug。他已经两岁了。人们写道:“我的带有IndexedDB的bfcache无法正常工作,我该怎么办?” Firefox开发人员回答-它确实崩溃了,我们只是在重构过程中丢失了这段文字,好吧,让它继续。道德:即使编写Firefox,也要编写测试,否则一切都会不幸地结束。

还有一个有趣的因素是bfcache不可用-如果明确允许混合内容。这是什么?


幻灯片中的链接

假设您的页面是通过HTTPS打开的,但是您仍然可以通过HTTP加载某些资源,尤其是脚本。也就是说,您有一个非安全脚本,任何人都可以对其进行修改。


幻灯片中的链接

默认情况下,Firefox和其他浏览器一样,现在不执行此类非安全脚本。但是,如果这对您非常重要,那么您可以进入设置并允许执行该设置,因此,它将不会缓存此类页面。他会说-好吧,您告诉我不要执行代码,但是然后,不,不!



另一个调整是bfcache本身的大小。在此,默认值为负一。这意味着Firefox有多少内存,它尝试缓存太多页面。但是我们可以通过置零或显式设置一个数字来强制禁用缓存-例如,记住不要超过五个页面。

警告:下一张幻灯片包含可怕的C ++语言的示例代码,这在前端会议中可能很危险。不要尝试复制它,而是在浏览器控制台中运行它。您的磁盘可能已格式化,屏幕可能会爆炸,或者可能开采了比特币。我警告过你。


幻灯片链接

因此,壁虎代码。可以在Internet上免费打开,阅读和查看它。而且我翻遍了。有最重要的方法-CanSavePresentation(),它回答了问题:是否可以缓存此文档?也就是说,这是关于Firefox现在实现的最终真理。但是-从那里,我得知您可以阅读日志。有这样的变量-gPageCacheLog。这是写入所有内容的日志。这是一个关于C ++游览的有趣故事。

也就是说,您打开链接,查看代码,进行搜索(这是一种方便且快捷的搜索方式),您可以在最新版本的浏览器中找到实际的实现详细信息,而这些正是文档中所缺少的。

苹果浏览器


当页面到达bfcache时,Safari所做的最残酷的事情:如果您有待处理的AJAX请求,Safari只会杀死它们。



即使您用错误处理程序覆盖它们并尝试检查所有内容,也请对其进行纠正-似乎请求根本不存在。从bfcache恢复后,您将没有错误,没有“确定”,什么也没有。



正如我所说,在Safari中,pageshow pagehide的处理程序只有在以内联到页面的脚本中编写时才被调用。从外部脚本来看,它们可能有效或无效-多么幸运。但是我警告过你



另一个有趣的区别是:beforeunload和unload处理程序不会干扰进入bfcache的页面。在这种情况下,始终会调用beforeunload,以及何时将其进入缓存以及何时未命中。但是当页面命中缓存时不调用卸载。这是另一个原因:该页面可能会进入缓存并从缓存中消失,并且如果您在卸载时编写了一些重要代码,则即使页面上未发生任何错误,也可能永远不会调用它。也就是说,它正确地进入了高速缓存,并且从高速缓存无处到达,并且永远不会调用您的卸载。



另一个有趣的观点。如您所知,您可以通过window.open()打开多个窗口。同时,这些窗口彼此之间具有链接。也就是说,它们可以同时爬到彼此的窗口中,读/写不同的属性。子窗口的这种打开不会干扰bfcache。在这里,很可能Safari和AJAX请求一样残酷,也就是说,一切都在艰难地告别。苹果开发人员更喜欢它。

再来一分钟C ++! WebKit的来源也不是秘密的,它们也可以被打开,阅读和研究。


幻灯片中的链接

它们在GitHub上,我重点介绍了两个重要功能:

-canCacheFrame()-检查是否可以缓存此框架。
-在页面上的不同对象(例如HTML元素或字体)中,有canSuspendForDocumentSuspension()方法。此对象负责它是否可以缓存,冻结。

还有一个重要方面:WebKit具有非常方便的测试。在LayoutTests / fast / history文件夹中,以小巧,紧凑,简洁的html格式显示了其中的测试,这些测试针对使用bfcache在Safari中实现的各种API进行测试。这是一个生动的示例,说明了如何使用这些API在Safari中编写代码,以及它们如何影响或不影响它们是否允许bfcache命中。看到很有趣。



从那里我了解到Safari还将有关bfcache的所有知识,所有功能写入了文本日志。但是,不幸的是,我从未发现如何启用此日志记录,或者如果启用了日志记录,则找不到在磁盘上找到该文件的位置。如果有人知道,告诉我,我将非常感谢。

铬。




正如我已经说过的那样,仍有工作在进行中,所有工作都已关闭。您可以下载新鲜的Chrome Canary,然后转到标志。该设置隐藏在此处-您可以尝试使用它。您可能会看到一些东西。



有趣的是-我已经谈到过通过window.open()打开页面。在Chromium中,此类页面到目前为止尚未缓存。他们没有弄清楚如何正确解决所有这些问题,因此无耻地将其切断,因为在Safari中,出于良心不允许。

如果没有发生DOMContentLoaded,则该页面也不会被缓存。

有许多以“ Web”开头的新API-很难理解也很难理解,到目前为止,所有这些API均默认关闭bfcache。也就是说,如果在页面上使用了一些时髦的新内容,例如WebGL,WebVR,WebUSB,WebBluetooth等,则此类页面将不会进入bfcache。

服务人员。此外,我们尚未缓存此类页面,但我们计划正确处理它,将其隐藏在服务人员的警惕范围内。

如果启用了地理位置,我们也不会缓存它。现在更简单。

如果页面在缓存中时,cookie变烂了,我们认为某种授权已过期。也许是网上银行或其他。这意味着该页面不再有效-我们从缓存中清除该页面。


幻灯片链接

Google家伙走得更远。他们建议我们统一所有形式化的东西,在所有浏览器中统一,建议所有状态的页面生命周期规范,建议在不同状态之间的转换中添加新事件。您可以查看他们在那里的链接。


幻灯片链接

资料来源。如您所知,铬源也是可用的。所有这些都位于一个名为BackForwardCacheImpl的类中-很好的命名,几乎不必看。检查文档是否可以保存的主要方法称为CanStoreDocument()。还有一个GetDisallowedFeatures()方法,该方法仅列出bfcache不支持的所有新功能和API。非常方便:集中一处,阅读并意识到当前可能发生的事情以及无法使用的事情。

Internet Explorer 11


对于那些仍然拥有IE 11的人来说,这是一次历史之旅。对于那些一切都不好的人。



那里有bfcache,这是主要问题,因为您必须处理它。该文档说bfcache应该只能在HTTP上运行。实际上,在生产中,由于某种原因,它也可以在HTTPS上运行。道德:如果您是开发人员,请注意您的文档。那你就因为她而受苦

如果有一个beforeunload处理程序,那么它将阻止它进入缓存。他们没有在文档中说任何关于卸载的事情-也许他们不知道或忘记了它。

如果页面尚未完成加载,则也不会缓存该页面。如果有人使用ActiveX组件,我们也不会缓存。如果DevTools也在那里打开。这是重要的一点。



如何没有错误?添加了持久字段,但有时不起作用。也就是说,页面实际上进入缓存,从缓存中返回,并且持久化未设置为true。该怎么办?

我们有漂亮的代码来确定是从缓存返回还是从服务器重新加载。



现在,必须为IE补充一个拐杖。我们确定我们拥有IE,并且在某些变通方法中,我们了解到该页面仍然是从缓存中提取的,同时我们还具有历史记录导航(back_forward)。



此外,您如何知道是否缓存了页面?我们花她的时间。如果它在50毫秒或更短的时间内从服务器加载,则基本上不能在IE中显示-这意味着它是从缓存中获取的! :)



我已经提到过历史导航。如果我们具有back_forward导航类型,那么我们将浏览历史记录,并且如果该页面来自缓存,则意味着bfcache,IE中没有其他选项。

下一步是什么?


下一步该怎么做?我不想让你出去像噩梦一样忘记这一切。

-首先,这是我遇到的最有价值的事情,也是我想带您去做的事情:使用开源浏览器!现在,在对Internet的开放访问中,是我们的客户使用的所有主流浏览器的源代码。这是有关如何以及如何支持它,在何处以及如何工作的最相关的文档。其中包括直接用HTML和JS编写的测试。使用,看!

-其次,在现有应用程序中考虑它们可以运行到bfcache中。告诉测试人员有关信息,以便他们在检查导航时知道在历史记录中导航时,可以从服务器和bfcache都打开该页面。这是当bfcache为我们工作时我们修复的实际错误的视频:

打开GIF

用户输入四个请求,看到四个问题。之后,它返回,看到问题3和查询4。另一个先前的问题是2和查询3。也就是说,它的页面状态不匹配-搜索和搜索字符串的内容彼此矛盾。在您的应用程序中记住这一点。

-第三,如果要编写新代码,请考虑是否需要bfcache。如果是这样,请使用API​​兼容性表。如果不是,请不要使用,但是如果您不小心进入bfcache,请考虑我提到的Safari和其他浏览器的功能。有些事情可能会令人发指,而您将无法理解为什么会发生这种情况。

如所承诺的,链接到材料。

All Articles