根据需要使用polyfill-service进行聚填

273 阅读6分钟

在另一篇文章"什么是polyfill "中,我谈到了我遇到的在IE10上出现白屏的情况(应用程序崩溃是因为我们缺少polyfills)。我解释了polyfill和代码转换之间的一些区别。我解释了一些你可以利用的选项,以使用新的JavaScript功能,并仍然支持旧的浏览器。在结论中,我这样说道:

那么我做了什么来修复我的IE10错误呢?好吧,有一件事让我很不爽,那就是我必须把所有这些用于polyfills的代码运送到所有的浏览器,即使它们确实支持这些功能。但几年前,我听说有一种服务,能够运送只与请求它们的浏览器有关的polyfills。我创建了自己的端点,使用了支持该服务的模块,我将在下周写下这篇文章

这就是这篇文章!我将解释我是如何创建一个polyfill.js 端点的,该端点将回馈一个非常积极的缓存的JavaScript文件,其中包含用户需要的polyfills,而不是更多。

按照我今天配置polyfill-service的方式,如果我使用Internet Explorer 10(我们支持的最低版本的IE)来请求polyfill.js ,响应是60.2kb!如果你不熟悉这可能造成的影响,我建议你阅读Addy Osmani《JavaScript的成本》(或在这里观看讲座版本)。用你可能理解的术语来说,在新兴市场的用户光是下载就需要整整一秒钟,然后你还得把他们下载的内容进行解析/编译/运行,这可能需要更长的时间,尤其是对于使用低端手机的人。

polyfills的现状是将这些polyfills包含在你的bundle.js文件中(事实上,很多应用程序只是使用所有的core-js ,这是84.2 kb的minified JS)。这意味着每个浏览器都需要下载、解析和运行该JavaScript,无论他们使用的是什么浏览器。但是我们来看看浏览器的使用统计。你的统计数字可能因你的用户而异,但如果你的应用程序是全球平均水平的典型,那么你可能有5%的用户需要超过少量的kbs价值的polyfills。你的大多数用户将使用现代的、常青的浏览器,支持你所使用的大多数功能。所以你让运行现代浏览器的用户为你的网站支持那5%不会/不能升级的用户而支付 "税"。

如果我在我的polyfill.js 文件上运行Chrome 67,它的结果基本上是空的。通过使用polyfill-service,只有那些需要polyfills的浏览器才会收到它们。这意味着他们可以更快地使用我的应用程序,而且我不会占用你的一些带宽来下载你不需要的东西(如果人们没有无限的数据,这实际上意味着可以节省实际的美元)。

使用像polyfill-service这样的东西的另一个方面是,因为我的polyfills生活在一个与我的bundle.js 完全不同的文件中,我可以让它永远被缓存起来,所以用户只需要下载一次就不需要再下载。因此,即使对于网络不好的用户来说,他们也会因为不必花费资源重新下载一个永远不会改变的文件而受益。

金融时报》的polyfill.io服务非常棒,但由于没有SLA(服务水平协议),许多公司不能依靠它来处理关键任务的应用。幸运的是,为其提供动力的模块是完全开源的,所以你可以在内部以一种相当直接的方式建立自己的服务,这正是我所做的。

在我现在工作的应用程序(paypal.me)中,我们有一个服务器,负责一些轻度的服务器渲染,以达到SEO的目的。基本上,我们的服务器是一个使用KrakenJS的NodeJS服务器(Express上面的一个封装器),所以我在Express应用中添加了一个get处理器:

app.get('/polyfill.js', getBrowserPolyfill)

而与getBrowserPolyfill 是一个典型的express路由处理程序:

import polyfill from 'polyfill-service'

async function getBrowserPolyfill(req, res) {
  const script = await polyfill.getPolyfillString({
    /* options */
  })
  res.set({
    'Content-Type': 'application/javascript;charset=utf-8',
    'Content-Length': script.length,
  })
  if (shouldCacheAggressively) {
    res.setHeader('Cache-Control', 'immutable')
  }
  res.write(script)
  res.end()
}

还有一些内容,但这是基本的想法。那么让我们来谈谈这个解决方案的几个方面。

因此,polyfill-service模块需要知道用户代理字符串是什么,以确定script 字符串应该是什么(要包括哪些JavaScript polyfills)。所以我把req.headers['user-agent'] 作为这个值,尽管我允许ua 查询字符串覆盖这个值,并且我有一个回退到IE 9的功能以备不时之需。如果polyfill-service遇到了它无法识别的用户代理,我将其配置为将其视为需要所有polyfills(通过unknown: 'polyfill' 选项)。

polyfill-service支持的功能有很多。它默认为最有用的功能,但配置它是一个好主意。起初我想。"嘿,让我们让它支持所有的东西。"但后来我发现,如果你要求它对所有能支持的东西都进行polyfill,它就会变得很大(主要是因为它实际上支持Intl ,每个语言包都有点大)。所以我最后指定了es2015,es2016,es2017,es2018, 和default-3.6 作为功能配置。这样做很好,支持所有我想支持的东西。

这个有点儿意思。因此,shouldCacheAggressively 是有点危险的,所以我是这样做的...因为我们正在对页面进行服务器渲染,我实际上可以为polyfill生成URL。它最终看起来像这样(针对IE 11):

polyfill.js?v=2&ua=Mozilla%2F5.0%20(Windows%20NT%2011.0%3B%20WOW64%3B%20Trident%2F7.0%3B%20rv%3A11.0)%20like%20Gecko

这里有两个查询字符串:v ,它与我硬编码的version 相关。这使我能够在紧急情况下打破缓存,如果我们需要改变配置或其他东西。

我还用ua ,它是用户代理,作为polyfill.js 文件的查询字符串的一部分。还记得我前面提到的我允许uq查询字符串覆盖req.headers['user-agent'] 吗?所以这就是我所做的。我这样做的原因是为了缓存。有了这样一个特定的URL,我可以安全地永远缓存它。如果用户升级(或降级!)他们的浏览器,但缓存没有被删除,那么这个URL就会被改变,旧的缓存版本就不会被使用。

我在构建这个过程中遇到了一个"有趣 "的经历,就是polyfill-service与babel编译类的方式不协调。请关注twitter上的话题和github上的问题,以获得你自己的 "快乐 "时光......

我对此感到很兴奋,我希望建立一个更正式的polyfill服务,让更多的PayPal应用程序使用它,这样人们就可以用最新的JavaScript功能来构建应用程序,而不用担心旧的浏览器是否原生支持他们所写的东西,也不用让现代浏览器的用户为旧浏览器的用户支付 "税"。

祝你们好运!