浏览器兼容问题不得不说的痛

761 阅读5分钟

刚开始写前端的时候,最头疼的是浏览器兼容问题,要思考的场景太多,常常无从下手。我曾经幻象,我弄个Excel,列出个12345,我就不信不能穷举Ta,后来,是我太天真。

很古老的一张截图.png image.png

今天讲的浏览器兼容问题可以这样描述:

在FireFox 47版本下,功能页白屏。

简单直接,毫无废话,用户反馈问题,真实,有力,一针见血。

我们现在用的Firefox是多少版本?

image.png

没错,截止(2021-08-11),FF的最新版是90.x。用户的电脑上还是47.0!

image.png

这,一言难尽。

和产品碰了下,了解下大概的问题现象和复现步骤,真实情况是这样。

某业务受限于Oracle,当前系统必须保持使用ff47.0,并且唯一可用。

打开控制台,错误是这样的。

SyntaxError: missing = in const declaration

image.png

(更糟糕的是,打包还没有做切割,全部都在一个文件内)

接下来,带着大家一起来抽丝剥茧的看看它。

问题分析

有明确错误提示的问题,通常来讲,定位问题相对都比较容易,这个问题就属于这种比较容易定位的。

我们去找一下,第一个检索项就是我们想要的。

image.png

即,使用const声明了一个变量,但是没有赋值。FF47认为这是一个语法错误。

// 错误的示例
const color;

// 正确的示例
const color = 'red';

不同的浏览器,有不同的提示。

SyntaxError: Const must be initialized (Edge)
SyntaxError: missing = in const declaration (Firefox)
SyntaxError: Missing initializer in const declaration (Chrome)

解释得很清楚,我们回过头看看代码,到底是哪里使用的不正确。

糟糕的是,打包没有拆包,所有内容全部堆在了一坨,不能通过链接快速定位具体代码行数。

直接简单粗暴,把所有代码里的const全部换成let,快速看下是否会报错。

很显然,错误依旧。

我突然想起来,项目使用ts,有错误会及时反馈,所以报错的不是我们自己的代码,一定是node_modules中的某一个或某些文件出现的问题。

暂时把兼容问题放下,先对代码库进行优化,至少要做到基础splitChunks优化。

项目中使用的是vue-cli4,对应webpack4,引入optimize-css-assets-webpack-pluginuglifyjs-webpack-plugin,设置minimizer,splitChunks,配置输出filename,chunkFilename

npm run build确认打包符合要求没有问题后。

重新看下开发环境的报错信息。

image.png

(内心独白:再聪明的狐狸也斗不过狡猾的猎人,手动狗头)

果然,在vue框架的runtime esm bundler包中,鼠标点点,看看源码是个啥,此时此刻,我洋溢着喜悦的神情,一副小人得志的嘴脸。

代码中一共118处const的使用,发现几乎每一处的语法都是正确的用法,特殊一点的只有这样写的

for (const key in prev) {
    // something...
}

我记得刚刚在哪里见过这样的问题,对的,是检索结果的第二项内容

image.png

答案显而易见,这是FF浏览器的bug。 image.png

Bindings -> const -> for-in loop iteration scope. image.png

clone compat-table ,修改environments.json配置信息。firefox47:{obsolete: true}即可看到已废弃的浏览器兼容情况 image.png

既然是不支持for (const x in key) 这样的语法,那我们就把文件@vue/runtime-dom/dist/runtime-dom.esm-bundler.js中用到的语法,全部替换为for (let x in key),看看会不会有变化。

image.png

runtime-dom.esm-bundler.js变成了runtime-core.esm-bundler.js

证明我们的修改点是正确的,那我们就顺坡下驴(手动狗头),全给整一遍。

依次操作如下文件:

@vue/runtime-dom/dist/runtime-dom.esm-bundler.js
@vue/runtime-dom/dist/runtime-core.esm-bundler.js
@vue/reactivity/dist/reactivity.esm-bundler.js
@vue/shared/dist/shared.esm-bundler.js
vue-router/dist/vue-router.esm-bundler.js

有谁会不喜欢干干净净没有红叉叉的console呢,至于那个warning可以忽略,性能什么的不重要(再次狗头)

image.png

至此,问题分析结束。

但,我们要上线,不能每次都这么搞一遍,要有一些舒服的方式才可以。

流程优化与反思

优化流程

  1. 优化打包构建流程,输出文件。
  2. 区分对待不同版本浏览器的请求文件,特殊版本的请求,特殊处理。
  3. 编写脚本,替换打包输出后的文件,仅替换特殊版本请求的vendors.xxxx.js文件。
  4. 写好说明文档,告知为什么要这样做。

4个步骤,也挺麻烦的,有没有其他更简便的解法。

正确解法

我们翻翻vue cli的文档

image.png

既然const语法是es6的,那我们干脆转成es5吧(谁让你要支持不支持ES6的浏览器呢)

vue.config.js增加如下配置:

transpileDependencies: [
    /[/\\]node_modules[/\\][@\\]vue[/\\]/,
    /[/\\]node_modules[/\\]vue-router[/\\]/
]

问题完美解决。

为什么vue2中没有这个问题

对于Firefox 47这个特定的浏览器版本,我们有一些产品也在支持,技术栈是vue2.x,甚至是FF43也支持,为什么没有出现这个问题?

看下源码或者dist后的产物,vue2没有esm bundler,最重要的是有各种xxxx-loader预处理器。

同时,我测试了同组的其他产品,在FF47也有同样问题(同vue3.x + ts)。

浏览器兼容情况

我们曾经梳理过,产品要支持的浏览器版本,并且长期以此为基准对齐,要求产品在开发期间,保证各个浏览器的稳定性,最大限度的满足兼容要求。

但不是所有的产品都能满足这个清单,总有一些特异化的需求还必须满足的,所以就有了本篇的这个问题。

结语

浏览器问题一直是我们的痛,前端同学经常要去面对,去解决。如果用户群体浏览器差异性比较大,这种现象更甚。我们寄希望于未来,期待大一统的融合和对齐。另一方面,也验证了js的强大,只要js支持的,早晚会用js来写。

any application that can be written in JavaScript, will eventually be written in JavaScript.