分类
js 技术

基于 @vue/cli 的项目配置 browserslist

前些日子虽然写了 最近折腾 @babel/preset-env 的一些小心得,但其实没有正确的理解和配置 browserslist,所以今天问题又来了。

同事问我,在项目 A 中使用内部库 B,A 里面需要继承和扩写 B,结果运行时报错:

TypeError: Class constructor c cannot be invoked without 'new'

看起来应该跟 ES6 Class 有关,报错的位置是构造函数,调用了 super(),我初步猜测:B 在编译时配置不当,导致编译后的文件被转换成了 ES5,也就是基于原型链继承的结构,所以在子类 super() 的时候报错。

接下来验证,打开 node_modules/B/dist/index.js,查找定义 class 的代码,出乎意料,B 的生产代码的确是 ES6 的,问题似乎不在这里。然后我把 import {ClassB} from 'B' 改成 import ClassB from 'B/src/ClassB',问题依旧,看来的确不是 B 错误编译造成的。

于是回到项目 A。项目 A 是使用 @vue/cli 创建的项目,里面 Babel 相关配置是默认的,预制组写在 babel.config.js里,是 @vue/cli-plugin-babel/preset;目标浏览器写在 package.json 里,内容如下:

{
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}

按照我之前的理解,这里的意思是:

  1. 市场占有率大于1%
  2. 浏览器最新的两个版本
  3. 二者需要同时满足,是且的关系

但实际上,在命令行里执行 npx browserslist,可以看到当前要支持的浏览器列表(其实仔细一看,这个列表和 最近折腾 @babel/preset-env 的一些小心得 里的是一样的……):

and_chr 76
and_ff 68
and_qq 1.2
and_uc 12.12
android 76
baidu 7.12
bb 10
bb 7
chrome 77
chrome 76
chrome 75
edge 18
edge 17
firefox 69
firefox 68
ie 11
ie 10
ie_mob 11
ie_mob 10
ios_saf 12.2-12.3
ios_saf 12.0-12.1
kaios 2.5
op_mini all
op_mob 46
op_mob 12.1
opera 62
opera 60
safari 12.1
safari 12
samsung 9.2
samsung 8.2

今天再看这个列表,感觉有些奇怪。别的不说,bb 和 ie_mob 应该都是非常古老的浏览器,以我对身边人的观察,相关设备早就已经退出历史舞台了。结合今天 class 的诡异报错,我觉得问题就在这里。

一般这种时候先试一下极端手段。我把浏览器改成了 chrome >= 75,指定只要最新浏览器,然后再次执行 npx browserslist,输出的结果果然只有最新的 chrome:

chrome 77
chrome 76
chrome 75

然后在项目中尝试刚才的操作,果然 bug 消失了。

再把 last 2 version 加回来,所有浏览器都回来了,bug 也如期复现。

好吧,看来之前对 browserslist 的理解是错的,定义项的关系是“或”,也就是所有筛选出来的浏览器取合集,所以我们不知不觉间其实在支持“所有浏览器的最后两个版本”,所以为了兼容那些古老浏览器,会把 ES6 class 转译成 ES5 的原型链格式,于是无法正确继承。

请注意:query 里面的 not 系列是“且”的关系,也就是从大集合里剔除某些项目的意思,比如 not ie <= 10 就是不考虑 IE 10-。

最后,我们把配置改成了兼容主流桌面浏览器,问题解决。

{
  "browserslist": [
    "last 5 chrome versions",
    "last 5 safari versions",
    "last 5 firefox versions",
    "last 5 edge versions"
  ]
}

babel.config.js

我在做上述事情的同时,我的同事也在做类似的尝试,不过她的目标是 `babel.config.js`。很遗憾,她没有成功。从文档来看,其实除了 browserslist,其它配置都在这个文件里……


总结

相比于早些年,前端工具链大幅提升了我们的开发效率,也带来不少新知识需要学习。根据我日常学习经验来看,这方面的分享比较少,而且流于表面;另一方面,这些工具链的文档阅读量不小。所以建议大家坚持学习,每次解决一些问题,慢慢的,就能把整个前端工具链都摸清楚了。

不过我没想通的是,为啥 @vue/cli 默认的目标浏览器设置的这么保守……

由meathill

爱编程,爱旅游,爱吐槽。
今年的目标是完成并运营至少一个 Side Project。
《Electron + Vue 实战开发》龟速创作中……

欢迎吐槽,请勿装死

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据