VWO非常重视确保网站在使用VWO时保持足够的性能。我们一直在加大这方面的努力,正因为如此,我们能够带来两个非常大的优化,这将直接减少对网站性能的影响。
去除jQuery的依赖性
VWO主要曾经是一个A/B测试工具(现在是一个完整的体验优化平台),客户可以使用VWO的编辑器对他们的网站进行修改,而不必对网站代码进行修改。为了实现这一目标,我们需要一个在所有浏览器上都支持的DOM操作和遍历库,而jQuery是当时的最佳选择。
多年来,它一直为我们服务,但我们无法更新jQuery,只能使用它的1.4.2版本,因为我们为客户提供了一个功能,他们可以使用jQuery的任何API(由VWO作为vwo_$暴露)。如果客户的网站已经使用了jQuery,它允许客户在页面上不包含jQuery两次。这在当时是正确的选择,因为jQuery非常流行,几乎每个网站都有它。但是当人们开始使用jQuery的各种方法时,升级jQuery版本就有了风险,因为我们不能确定客户使用的是什么方法,以及这些方法的API在新版本的jQuery中做了什么改变。
为了提高库的性能,我们决定编写一个基于cash.js和jQuery.js的小型jQuery替代品,这个新的库大约13KB(最小化和未压缩),而jQuery 1.4.2的库是70.5KB(最小化和未压缩)。
为什么不是jQuery
-
jQuery是巨大的未压缩的大小(70.5KB)。
- jQuery需要照顾到所有浏览器的怪癖,在所有可能的方式,一个方法被调用。
-
我们也没有使用它所提供的所有功能。
- jQuery提供了很多方法,但并不是所有的方法都是我们所需要的,例如,我们不需要CSS中的动画支持
- 它提供了一个自定义的构建器,允许用户跳过某些模块,但由此产生的JS大小仍然很多,因为它有不需要的东西不被我们所使用。
因此,解决方案是创建我们自己的jQuery的替代品,这将满足以下要求:
- 一个非常定制的解决方案,只实现VWO所需要的功能--使代码大小尽可能的小,支持IE11+和所有其他浏览器(Chrome/Firefox/Opera/Safari/Edge)。我们使用Browserlist来识别浏览器的份额,并决定不支持百分比份额低于1%的浏览器。
- 它应该符合jQuery的API,这样在使用jQuery的时候几乎不需要改变代码。
- 它应该像jQuery一样稳定。
我们采取了哪些步骤来迁移到 "无jQuery"?
-
我们开始扫描整个库的代码库,用正则表达式搜索匹配vwo_$().methodName的调用,能够得到一个被VWO使用的jQuery方法列表。
-
我们发现了一些只是 "语法糖 "的方法被使用,例如,有first()方法,在技术上相当于eq(0)。因此,我们将first方法的用法改为eq(0)。
-
我们开始从最新的jQuery版本中获取方法的实现,并开始删除那些不适合我们的代码。
-
我们采用了cash.js的测试,并运行其测试。我们选择了cash.js的测试,因为它的功能是jQuery的一个非常小的子集,主要是我们需求的一个超集。很多测试都失败了,我们确定了哪些失败是可以接受的,哪些是不可以的,并相应地修改了测试。
-
我们将新的库与我们的代码库集成并进行测试。除了使用jQuery的库的单元测试之外,我们还有大量的集成测试和E2E测试。尽管我们现有的单元测试在这里没有帮助,但集成和E2E测试的良好覆盖率有助于发现错误。
-
除了自动测试,我们还在不同的浏览器和平台上对所有的库做了大量的手动测试,这需要很大的努力。
-
一旦所有测试通过,我们决定为vwo.com启用这个新库,因为该网站使用VWO本身来监控生产中的错误:
- 这就需要在生产中使用各种设备和浏览器来测试这个库。
- 该网站有哨兵来跟踪网站上的任何错误。
- 我们对错误进行了几天的监测,并能够从中发现一些我们的自动化和人工测试都无法捕捉的错误。
-
在这些错误被修复后,我们又监测了几天,以确保安全,然后我们为所有注册VWO的新用户启用了它。对现有的账户启用它是不可能的,因为他们可能直接使用jQuery的某些方法。
-
接下来,我们将研究一种方法来可靠地识别可以自动启用新库的账户。
下面是一个静态和本地网站(我们选择这样做是为了消除网络波动)使用我们库的两个不同版本的性能统计。所有的统计数字都是我们做的五个性能审计的中值。
| 互动时间 | 第一次CPU空闲 | 性能得分 | |
|---|---|---|---|
| 使用jQuery | 6.1 | 5.9 | 74 |
| 不使用jQuery | 5.4 | 5.1 | 79 |
对所有静态文件进行Brotli压缩
Brotli是由google推出的一种压缩技术。它是一种无损压缩算法,通过使用LZ77算法、Huffman编码和二阶上下文建模的组合来压缩数据。它在速度上与deflate相似,但提供更密集的压缩。
为什么是Brotli?
它被所有流行的浏览器所支持。据报道,它比gzip压缩有高达25%的收益。这些信息足以让我们专注于在我们的CDN上实施Brotli。在网络上传输更少的字节,不仅会导致页面加载时间的减少,而且还会降低服务文件的成本。
为什么只是静态文件?
我们发现,即时压缩资源不会直接导致性能优化,因为与gzip相比,Brotli的压缩时间较长,可能会影响响应时间。更多细节见blogs.dropbox.com/tech/2017/0…
我们为静态文件从gzip转移到Brotli所采取的步骤
Brotli压缩是一次性为我们所有的客户启用的(每个账户策略是不可能的,因为静态请求不包含VWO账户ID),我们必须特别确保在任何情况下我们客户的数据都不会受到影响。因此,我们仔细遵循以下步骤:
-
在基于NodeJS的构建过程中,我们用最高级别的压缩等级(即11级)压缩了所有的静态文件。
-
建立的文件包含三个版本的文件
- 未压缩的
- Brotli压缩 (.br)
- Gzip压缩的(.gz)
-
早些时候我们使用github.com/foliojs/bro…,但我们发现它不能压缩小文件(https://github.com/foliojs/brotli.js/issues/19)。因此,我们转移到github.com/MayhemYDG/i…我们的自动化系统发现了这个错误。以后会有更多关于自动化的内容。
-
-
我们的CDN使用OpenResty,我们已经在Lua中制定了一些重写规则,以便能够向不同的浏览器提供不同的内容。在那里我们增加了对已经压缩的brotli文件的支持:
- 我们读取 "Accept-Encoding "头,并从那里确定UserAgent所支持的编码。
- 如果UserAgent声称支持brotli,我们就提供brotli,否则我们就提供gzip。我们假设没有浏览器不支持gzip,这一点在caniuse.com/#search=gzi…中得到了验证。
- 我们确保了Vary:Accept-Encoding 响应头在所有情况下都被设置。稍后会有更多关于这个的内容。
-
在进入生产阶段之前,我们想确保在生产阶段,所有声称支持brotli的浏览器都能以最高的压缩级别进行解压。为此,我们决定压缩我们最常用的库,并将其作为一个独立的请求提供给vwo.com。我们确定了库中的一个特定字符串,并确保它每次都存在。如果它不存在或者响应代码不是200,我们就把它作为一个错误记录在Sentry上。我们监测了2天的日志,没有发现问题。所以,从这个角度看,一切都很好。
-
由于VWO库的发布过程相对复杂,为所有可能的静态文件创建brotli文件并不实际[我们对所有文件都有版本管理]。由于这个原因,我们不得不修改Nginx的conf位置块,强制任何对废弃文件的请求重定向到该文件的最新稳定版本。这需要对所有可能的静态内容服务端点进行测试。
我们是如何确保发布时没有问题的?
为了确保新版本的VWO库没有问题,除了现有的e2e测试案例外,我们还为我们的CDN写了基于请求响应的自动化测试案例。我们通过自动化扫描我们所有的库构建的代码,创建了一个由我们的CDN提供的所有可能的静态文件的列表。我们把它与我们的库的所有可能的版本结合起来,并创建了一个所有可能的端点的列表(包括一些不存在的端点,因为一些文件在某些版本中不能被服务)。
它对所有这些端点进行了以下验证:
- 对于Accept-Encoding请求头文件的不同变化,预期的压缩被提供。通过检查Content-Encoding Response Header验证了预期的压缩。
- 状态代码是200。如果状态代码为非200(记住其中有不存在的端点),它将与指定的生产CDN节点之一进行验证,看其是否也返回相同的非200状态代码。如果是,那么它就是一个不存在的端点,否则就是一个错误。
- 我们有像dev.visualwebsiteoptimizer.com/6.0/va.js 的端点。这里6.0意味着6.0版本的库。我们用框架来确保请求的内容与请求的版本相对应。我们几乎所有的文件中都有版本信息。
- 奖金,我们利用框架实现中所做的努力,也为我们所有的端点验证了许可证。另外,该框架足够灵活,可以在响应中验证任何东西。
我们并没有在所有的CDN节点上一次性地进行修改。我们从流量最小的一个节点开始,进行仔细的监测。我们监测了一天的日志,然后在其他的服务器上也进行了直播。
我们的测试库--使用最多的[尺寸是在jQuery不存在的新版本中]
Vary头的重要性
为了确保任何HTTP缓存不会缓存一个特定的压缩文件,并为所有的用户代理提供该文件,无论该用户代理是否支持解压,我们正在使用带有Accept-Encoding的变化头来确保正确的文件被提供给用户代理。你可以在www.fastly.com/blog/best-p…上阅读更多关于它的信息。
未来计划
目前这个库只对新客户开放。但我们正计划为我们所有的现有客户部署这个库。这需要弄清楚他们是否直接使用任何jQuery方法。 此外,我们将试验在飞行中对我们的非静态文件进行brotli压缩。
这并不是我们性能改进之旅的结束。请继续关注!