什么是 polyfill?
我们用 MDN 上的原话进行一段话解释:
Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。
在此基础上,polyfill.io 提出了一种「在线引入」的方案。我们用 polyfill.io 的官网介绍:
It’s a service which accepts a request for a set of browser features and returns only the polyfills that are needed by the requesting browser.
它是一种服务,接受对一组浏览器功能的请求,只返回请求浏览器所需要的polyfills。
此外,polyfill.io 还开源了 polyfill service 方案,允许我们自己搭建一个 polyfill 服务。
引入 polyfill.io
引用是非常简单的,选择你需要引入的 polyfill,然后拼接成一个 url 即可。这个 url 会根据当前浏览器的版本,自动获取这个浏览器版本所需要的 polyfill。
例如:
<script src="https://polyfill.io/v3/polyfill.min.js?features=es2022"></script>
国内网络环境引入 polyfill.io
由于国内网络原因,会偶发性的遇到 polyfill.min.js 这个文件拉不下来或者拉取时间过长,导致页面白屏卡死的问题。我们可以通过以下方式来解决:
方案一:使用 CDN
自建 polyfill service 的成本比较高,并不适合所有人,这里就不多说了。
但可以用别人建好的,即 CDN。目前我能找到的只有两个 CDN:
- 官方 CDN:cdn.polyfill.io/v3/polyfill.min.js
- 阿里 CDN:polyfill.alicdn.com/polyfill.min.js(不推荐)
- 这个是我在一个 blog 下的评论找到的,评论时间 2019 年,不确定是否稳定。
- 而且我拿 chrome70 + es2021 试了一下,二者返回的数据明显不一致。
- 另一个阿里 CDN:polyfill.alicdn.com/v3/polyfill.min.js
- 这是我自己实验出来的,与 cdn.polyfill.io/v3/ 的返回值是一摸一样的。
- 但是也没有官方的文档来背书。
此外,还有个退而求其次的方案,也放在这里,但并不推荐。
就是基于一个最低的浏览器版本,按照这个浏览器版本去获取 polyfill.min.js 文件,然后把这个文件存在代码中。
不推荐的理由是,这样做并没有实现按需引入。
方案二:将 script 标记为 async
无论是自建服务,还是使用第三方 CDN,都有可能存在拉取时间过长的问题。但是 polyfill 并不是所有用户访问的必要条件(例如一些用户的浏览器就是最新版本)。所以应当允许用户在不在入 polyfill 的情况下,正常打开网站。
我们用一个表格来说明各种不同的情况下 polyfill 载入失败的影响:
用户的浏览器版本 | 用户正在访问的页面是否需要用到 polyfill | polyfill 载入失败后的情况 |
---|---|---|
新 | 是 | 无影响 |
新 | 否 | 无影响 |
旧 | 是 | 有影响,页面打开失败 |
旧 | 否 | 无影响 |
可以看到,在 polyfill 载入失败的情况下,只有当用户浏览器版本为旧版,且用户正在访问的页面也用到了 polyfill 的情况下时,才会出现页面载入失败。因此这并不是一个高出现率的事件。所以我们可以将这个脚本改为异步处理的。
异步处理的方式有两种,async 和 defer,其区别主要在于执行顺序。图示如下:
- 同步 script
- script 载入过程会阻塞渲染
- async
- 如果在渲染过程中载入成功,那么暂停渲染,执行脚本,然后继续渲染。
- 如果是在渲染结束后载入成功,那么会立即执行脚本。
- defer
- 一定会在渲染结束后执行脚本。
我们要考虑需要 polyfill 的情况。即这个页面的渲染依赖 polyfill,因此需要中断渲染,引入 polyfill,再继续渲染。所以必须要使用 async。
此外,还应该特别注意的是,页面的 layout 部分,尽量不要使用最高版本的方法,防止 polyfill 载入失败导致了整个页面不可用。