• 一文搞懂前端兼容问题


    对于大部分开发者来说,版本兼容是一件存在感很低的事情,因为它在绝大部分情况下都是一行配置,在一些前端工具链(例如 Babel、CoreJS,Autoprefixer 等工具)的帮助下适配到目标浏览器,只会在一些大的 break change 事件(例如 Vue3 必须在支持 Proxy 的现代浏览器下才能运行)下才会关注这件不起眼的事情。

    但当你稍微研究一下的时候,才会发现这块儿内容知识非常杂,因为版本兼容的相关知识没有那么多内在逻辑性,很多细碎的知识点散落在在各个商业公司的博弈和版本变更中。前段时间因工作需要对部分语言/浏览器特性重点关注了一下,以此文做一下记录。

    本文主要记录了移动端的版本适配问题,未对桌面端做更多的研究,后面有可能补充相关内容。

    1.iOS & Safari

    iOS 和 Android 虽然都是一年更新一个大版本,但受益于生态的封闭性,iOS 的更新率极高,基本上最近的两个版本就能覆盖 95% 以上的人群。

    例如 Apple 官方统计[1],截止到 2022-05-31,Apple 四年内推出的新机型中,iOS 15 已经有 89% 的装机率,iOS 14 也有 10% 的装机率;而在所有历史机型里,iOS 15 和 iOS 14 加起来也有 96% 的装机率,隔壁 Android 都羡慕哭了:

    为什么如此关注 iOS 的版本号?因为 iOS 版本基本上和 Safari 版本一一对应的,例如 iOS 15.6 上安装着 Safari 15.6iOS 14.5 上安装着 Safari 14.1,具体的映射关系可见 MDN 的映射表[2],或者可以看 core-js: SafariToIOS[3],所以我们基本上只要比对 iOS 版本号即可。

    另外一个问题是,有一定 C 端开发经验的开发者可能还会关注 iOS 上运行的是 UIWebView 还是 WKWebView,在 2022 年这个时间点其实已经不需要关注了,因为 Apple Store 已经发过公告[4],2020 12 月之后已经禁止含 UIWebView 的 APP 上架了,所以 iOS 平台只有 WKWebView 这一个 WebView 了,而且它兼容性也不错[5],最低支持到 iOS 8

    2.Android & Chrome

    说完 iOS 我们再谈谈 Android。因为两个操作系统的发展策略不同,再加上国内各大厂商的魔改,Android 从一开始就深陷碎片化的深渊,有 Android 开发经历的同学一定都深有感触。

    Android 系统本身碎片化,Android 系统自带的浏览器更碎片化。在 Android 早期,Android 版本和 Chrome 浏览器版本是有绑定关系的,那这个早期是有多早?那就是 Android 4,2014 年发布,绑定了 Chrome 早期几个版本,因为数据不多,我这里就直接列出来:

    1. // https://github.com/zloirock/core-js/blob/master/packages/core-js-compat/src/mapping.mjs
    2. ChromeToAndroid: [
    3.   [9'3.0'],
    4.   [12'4.0'],
    5.   [30'4.4'],
    6.   [33'4.4.3'],
    7. ]

    事情的转机出现在 Android 5,这个版本[6]里 WebView 被移植为一个独立的 APK,可以独立更新,不再和 Android 系统深度绑定。

    Google 的想法初衷是好的,借助于 Google 商店,解耦更利于浏览器的版本迭代。但是在国内多厂商魔改和一些网络问题上,极有可能发生这样的事情:对于一台 Android 5 手机,理论上用户可以安装 Chrome 36 - Chrome 95 任意一个版本

    所以和 iOS 对比起来,Android 因为他的开放性带来严重的碎片化问题:Android 版本多样,Chrome 版本多样,还有各种魔改内核,对于开发者来说适配起来真的苦不堪言。

    3.Web Browser

    了解完操作系统的版本历史,我们再看看浏览器上最关键的 JavaScript 语法兼容度。

    JavaScript

    最近十年内,JS 这门语言的的最大变革就是 ES6(ES2015)的发布了,带来了非常多的新特性。下面我搞了一个表格,列出几个大家常用的 JS 语法是从哪个版本开始支持的:

    语法/APIiOSChrome
    Class949
    =>1045
    const1149
    let1149
    Proxy1049
    generators1039
    Promise833
    async await1155
    import export10.361
    .........

    我们可以看到,这些语法的最低支持版本集中在 iOS 10iOS 11Chrome 49Chrome 61 这几个版本上,我们把它们的版本发布时间列出来:

    事件发布时间
    ES5 标准发布时间2009.12
    ES6 标准发布时间2015.06
    iOS 10 发布时间2016.06
    iOS 11 发布时间2017.06
    Chrome 49 发布时间2016.03
    Chrome 61 发布时间2017.09

    时间列出来后结论基本上是呼之欲出了:ES6 标准发布后的未来一年时间内,各大浏览器语法就支持的差不多了,两年后基本上就全部支持了这个时间点就是 2017 年,对应着 iOS 11  Chrome 61

    legacy vs modern

    看完 ES6 的支持情况,我们再来了解两个概念,「经典浏览器」和「现代浏览器」。

    这两个词在英文里对应着「legacy browser」和「modern browser」。如果大家比较关注一些相对前沿的前端项目,比如说 Vue3,Solidjs,Vite,它们的官网里其实经常提到这两个词。

    那么问题来了,既然有两个称呼,那在工程里必然存在一个分界线去区分 legacy 和 modern,这个分界线就是 iOS10.3 和 Chrome 61,既浏览器支持 ES Modules 的版本(支持