刚开始写前端的时候,最头疼的是浏览器兼容问题,要思考的场景太多,常常无从下手。我曾经幻象,我弄个Excel,列出个12345,我就不信不能穷举Ta,后来,是我太天真。
很古老的一张截图.png
今天讲的浏览器兼容问题可以这样描述:
在FireFox 47版本下,功能页白屏。
简单直接,毫无废话,用户反馈问题,真实,有力,一针见血。
我们现在用的Firefox是多少版本?
没错,截止(2021-08-11),FF的最新版是90.x。用户的电脑上还是47.0!
这,一言难尽。
和产品碰了下,了解下大概的问题现象和复现步骤,真实情况是这样。
某业务受限于Oracle,当前系统必须保持使用ff47.0,并且唯一可用。
打开控制台,错误是这样的。
SyntaxError: missing = in const declaration
(更糟糕的是,打包还没有做切割,全部都在一个文件内)
接下来,带着大家一起来抽丝剥茧的看看它。
问题分析
有明确错误提示的问题,通常来讲,定位问题相对都比较容易,这个问题就属于这种比较容易定位的。
我们去找一下,第一个检索项就是我们想要的。
即,使用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-plugin
和uglifyjs-webpack-plugin
,设置minimizer,splitChunks,配置输出filename,chunkFilename
npm run build
确认打包符合要求没有问题后。
重新看下开发环境的报错信息。
(内心独白:再聪明的狐狸也斗不过狡猾的猎人,手动狗头)
果然,在vue框架的runtime esm bundler包中,鼠标点点,看看源码是个啥,此时此刻,我洋溢着喜悦的神情,一副小人得志的嘴脸。
代码中一共118处const的使用,发现几乎每一处的语法都是正确的用法,特殊一点的只有这样写的
for (const key in prev) {
// something...
}
我记得刚刚在哪里见过这样的问题,对的,是检索结果的第二项内容。
答案显而易见,这是FF浏览器的bug。
Bindings -> const -> for-in loop iteration scope.
clone compat-table ,修改environments.json配置信息。
firefox47:{obsolete: true}
即可看到已废弃的浏览器兼容情况
既然是不支持for (const x in key)
这样的语法,那我们就把文件@vue/runtime-dom/dist/runtime-dom.esm-bundler.js
中用到的语法,全部替换为for (let x in key)
,看看会不会有变化。
从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可以忽略,性能什么的不重要(再次狗头)
至此,问题分析结束。
但,我们要上线,不能每次都这么搞一遍,要有一些舒服的方式才可以。
流程优化与反思
优化流程
- 优化打包构建流程,输出文件。
- 区分对待不同版本浏览器的请求文件,特殊版本的请求,特殊处理。
- 编写脚本,替换打包输出后的文件,仅替换特殊版本请求的
vendors.xxxx.js
文件。 - 写好说明文档,告知为什么要这样做。
4个步骤,也挺麻烦的,有没有其他更简便的解法。
正确解法
我们翻翻vue cli的文档
既然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.