从@babel/preset-env
谈多浏览支持构建
最近需要把一个在特定浏览器环境运行的Web应用移植到多浏览器,特别是要支持部分IE浏览器。项目打包完成,在IE 11下运行,并不能成功,提示Map()未定义。很显然,IE浏览器并不支持ES6
语法,而在构建中也没有使用相应的填补。
在基于Webpack
+ Babel
构建的应用中,我们一般会使用到@babel/preset-env
这个包,它使用了各种工具转译我们编写的ES6
代码,我们一般这么使用它:
1 | // .babelrc |
不做额外配置的情况下,@babel/preset-env
并不知道哪些ES6+
的语法需要转译,所以最终的结果就是并没有转译。
@babel/preset-env
的targets
属性
为了兼容多浏览器,我们需要告知哪些应用构件时需要支持哪些浏览器,@babel/preset-env
提供了targets
属性进行配置,例如为IE 10
构建,可以这样配置:1
2
3{
targets: "ie 10"
}
我们可以使用browserslist
包来指定构建的目标浏览器,可以在.browserslistrc
或是package.json
中进行配置:
1 | "browserslist": [ |
上面的配置中包含4条查询语句,>0.2%
表示大于0.2%
的市场份额,not dead
表示近24个月还在支持的浏览器,所有查询语句见npm。
配置完成后,可以运行npx browserslist
查看具体支持的浏览器版本。
必不可少的useBuiltIns
属性
设置browserslist
后构建的代码仍然不能很好的运行,这是因为项目中并没有加入polyfill
,并且也未告知@babel/preset-env
该如何处理pliyfill
。useBuiltIns
正是用来解决这一问题。1
useBuiltIns: false | "entry" | "usage"
当useBuiltIns
设置为"entry" | "usage"
的时候,@babel/preset-env
将会使用core-js
提供的填补。
"entry"
的意思就是,当我们在某个文件中import 'core-js'
但是该文件中只使用了到了ES6
中的String.prototype.padStart
方法,那么就上一句import
就会在构建的时候被替换成import 'core-js/modules/es.string.pad-start'
。
"usage"
的作用同其字面意义,即为:按需加载。如果我们在某个文件中使用了Map
,如果构建目标支持Map
,那么就不会使用相应填补,否则会在构建时加上import 'core-js/modules/es.map'
。
现在设置useBuiltIns: "usage"
。
core-js
及其使用
core-js
是一个ES6+
语法的polyfill
,简单而言就是使用ES3
的语法实现了到目前为止几乎所有ES
新特性。并且可以按需加载且不会污染全局命名空间。
core-js
有2.x
和3.x
两个版本,其区别就是2.x
不支持目前最新的语法,这里可以按需选择2.x
或者3.x
版本安装。
安装core-js
:1
yarn add core-js
使用core-js
的需要注意的是该包需要在入口文件顶部导入,因为只有这样填补才会被完全用到。
对已使用webpack
构建的项目可以在config
中在入口中引入:1
entry: ['core-js/stable', 'index.js']
也可以在入口文件(一般为src/index.js
)的顶部引入:1
2// index.js
import 'core-js/stable'
引入完毕,再进行构建,不出意外已经能够在指定版本的浏览器中运行了。
其它
ES6+
语法的多浏览器兼容的确告一段落了,但是浏览器API
,CSS
兼容还有很多问题,浏览器兼容才刚刚开始。
比如,IE
并不支持fetch API
,所以要么我们需要一个polyfill
,要么就修改业务代码。1
yarn add whatwg-fetch
然后在入口文件中引入,1
import 'whatwg-fetch';
某些CSS3
支持得也不够好,需要我们一个个去考虑。
兼容性是前端开发无法规避的问题,而解决兼容的过程是”痛苦的”,特别是当业务开发完成后才考虑兼容的问题,痛苦加倍。
痛苦并快乐着。
完。