我正在参加「掘金·启航计划」
视频地址 babel7掌握这四个包就行了
对前端同学来说,Babel是一个需要攻克的痛点。
我们为什么对Babel
会疑惑呢?
-
1、Babel中有几十个包,我们做配置的时候不清楚到底要哪些包
-
2、网上关于Babel资料很多,同一个功能为什么不同文章用的包不一样
-
3、按照某些文章列的包安装后,并没有达到效果
我们首先应该明确Babel版本差异大
-
1、Babel发布时所有包都会发布,版本号都是一致的,即使有些包没有改动一行代码也会发布新版
-
2、Babel各大版本之间差异很大,目前我们是用
Babel7
做配置,Babel7
包名都是@babel/
开头的 -
2、
Babel5
和Babel6
包名不是@
开头,这些包就逐渐淘汰了
Babel就5个常用包 解决90%场景
强调一遍,这是Babel7就些包就够了,最新是7.19.1,之前的Babel6,Babel5都配置麻烦显得复杂
-
babel-loader
@8.2.5 -
@babel/core
@7.19.1 -
@babel/preset-env
@7.19.1 -
@babel/plugin-transform-runtime
@7.19.1 -
@babel/polyfill
@7.12.1
这是Web
应用的,后面我们会说针对js库
配置就不需要@babel/polyfill
,而是用@babel/runtime-corejs2
,这部分看文尾
装包后给下面三个文件配置上就完事
npm install babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
npm install @babel/polyfill
babel.config.js 配置
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "usage",
corejs: 2
},
],
],
plugins: ["@babel/plugin-transform-runtime"],
};
.browserslistrc 配置
last 2 versions
> 0.5%
IE 10
webpack 配置 loader
module: {
rules: [
{
test: /\.jsx?/,
exclude: /node_modules/,
use: 'babel-loader'
},
]
}
只是让Babel运行起来,到这里就结束了,还没用到5分钟吧,后面是讲一些原理,可作为深入了解
更深的展开来说
babel.config.js 配置解释
1、我们看到常用presets
和plugins
两个配置项,分别叫预设
和插件
,预设就是一些插件的合集,其他配置项minified
、ignore
等几乎用不到。
2、Babel针对常用的环境做了这些preset包,@babel/preset-env
,@babel/preset-react
,@babel/preset-typescript
,@babel/preset-flow
,所有预设都需要先安装才能使用。
3、插件可以写短名称,省略前缀 babel-plugin-
,如 babel-plugin-transform-decorators-legacy
,写作transform-decorators-legacy
,但是不推荐
4、预设可以写短名称,省略前缀babel-preset-
,@babel/preset-
,但是不推荐
5、plugins和presets的执行是有顺序要求的,插件比预设先执行
,插件执行顺序是插件数组从前往后
执行,预设执行是预设数组从后往前
执行
6、插件和预设都可以传参数,如果没有参数是这样写一维数组 presets:['@babel/preset-env']
,如果有参数是这样写 presets:[['@babel/preset-env',{ modules: false, useBuiltIns: "usage", }]]
,变成二维数组,第一项是字符串包名,第二项是参数对象
@babel/preset-env 的参数细讲
我们配置了两个参数 {modules: false, useBuiltIns: "usage"}
,这两个参数很重要通常来说必须配置
modules
1、modules 默认值是auto,可以取 amd\umd\systemjs\commonjs\cjs\auto\false,这个参数的作用是把ES6模块语法转换为其他模块语法
2、我们常用的模块化语法是esm用import和export,commonjs用require和module.exports,当 modules:auto 时是默认将import转换成require
,这将导致的问题是webpack构建时,丢失tree shaking等优化措施
,所以设置为false后,还是使用import引入模块
useBuiltIns
useBuiltIns用来优化生产代码体积,默认是false,可以取 usage\entry\false
为了搞懂useBuiltIns,我们必须先来说下另一个包@babel/polyfill
,有点需要明确Babel进行转换的时候是做语法转换而不做api补齐
,如把箭头函数转换为普通函数,把class转换等,但是如代码里写了Promise对象,但是在某些老浏览器中没有Promise对象,就直接报错了,通常在项目入口js文件import '@babel/polyfill'
就解决问题了,在window全局模拟出Promise对象等
,但是完整的@babel/polyfill
包很大,有所有新特性api,引入页面影响了加载,官网已经不推荐直接引入了
所以useBuiltIns就是来解决按需引入@babel/polyfill
,也不用安装这个包
1、配置false
时,需要在入口js手动引入polyfill,会全部引入代码
2、配置entry
时,需要在入口js手动引入polyfill,会根据browserslist设置的目标环境找到需要的polyfill部分引入,注意只能写一次引入,否则会报错
3、配置usage
时,不需要在入口js手动引入polyfill
,会根据代码中用到的ES6特性且目标环境缺失的polyfill部分自动引入,所以比entry更少
所以这里能看出来polyfill是在window全局对象的修改,这对web应用一般没问题,但是对于如果我们是做js库,那就可能产生问题,比如我们封装的库用的polyfill版本和web应用导入的polyfill版本不同,后面我们会说针对js库就不是用polyfill的方式了
corejs
corejs 默认2,取值2/3,在useBuiltIns设置为entry/usage才生效,通常默认用2就行,可以不设置这个参数
因为@babel/polyfill
用到了这个包,会自动安装core-js@2
版本,当某些很新api如数组flat方法时,core-js@3
才有时,需要将corejs设置为3,同时要手动安装npm install core-js@3
我们查看生成的输出文件,就是引入的core-js
来补充api
targets
参数targets和browserslist很像,用来设置Babel转码的目标环境,targets优先级高于browserslist,有targets参数就不用browserslist了,没有targets会自动去找browserslist,如果两者都没有配置,那就会把所有ES6转换成ES5,但是不推荐用这个参数
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets:{
ie:"10"
}
modules: false,
useBuiltIns: "usage",
},
],
],
plugins: ["@babel/plugin-transform-runtime"],
};
@babel/plugin-transform-runtime 参数细讲
我们刚才讲 @babel/polyfill
时说,它是修改全局window对象达到补齐api的目的,其实是有全局污染的,只是对于web应用来说一般没问题,但是考虑如果我们是在做js库,那这样修改全局window是可能出现大问题的,所以需要配置参数,不用@babel/polyfill
方式,而是用@babel/plugin-transform-runtime
也有补齐api,看最后生成的d.js
图中我们看 a.js
源代码文件包含promise和class
b.js
文件,我们把babel.config.js
的plugins项@babel/plugin-transform-runtime
去掉,然后生成的,可以看到主要是class被转换成了三个函数,这个三个函数代码很长增大了文件体积,而且我们项目几百个js文件每个都有这些函数,那代码量太大了,于是各种转换后的函数就封装到@babel/runtime
这个包里面了,@babel/plugin-transform-runtime
依赖@babel/runtime
包,作用就是把b.js
里面的函数替换成import @babel/runtime
的对应函数
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "usage",
corejs: 2,
},
],
]
};
c.js
文件就是我们90%场景下用到的web配置生成的,也是我们最上面babel.config.js
的配置生成出来的,问题就是如果我们做js库,这种配置会修改全局window,带来隐患
d.js
文件就是我们做js库是看promise不再是全局window下的对象,而是我们引入的_promise对象,不修改全局window,更安全可靠,就是给@babel/plugin-transform-runtime
传了参数corejs:2
,这里可以传2/3,对应的需要安装@babel/runtime-corejs2
或者@babel/runtime-corejs3
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "usage"
},
],
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: 2,
},
],
],
};
@babel/plugin-transform-runtime 有下面5个参数
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
corejs
默认值是false,可以取false/2/3,作用就是来避免polyfill的全局污染,在前端web项目里一般就是false不设置,如果是js库开发,我们设置为2/3,并安装@babel/runtime-corejs2
或者@babel/runtime-corejs3
helpers
默认值是true,作用是是否自动引入辅助包,当然肯定要引入啦,就是上面用import @babel/runtime
函数替换文件本身转换出来的class函数等
regenerator
默认true,一般不改变
absoluteRuntime
默认false,可以取false或字符串,一般不改变,因为@babel/plugin-transform-runtime
引入的@babel/runtime
包,默认是在node_modules
下,如果你@babel/runtime
在其他位置,这个参数就可以修改地址
useESModules
默认值false,该配置项用来设置是否设用es6模块化用法,启用时,转换使用的helpers
将不经过@babel/plugin-transform-modules-commonjs
插件处理。这将在类似 Webpack 等模块系统里产生更小的构建输出,因为它不需要保留 CommonJS 的语义代码。【这个我设置了在webpack打包出来看效果没有任何变化,目前我还不清楚影响】
比如,如下是禁用了useESModules
的classCallCheck
:
exports.__esModule = true;
exports.default = function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
启用后:
export default function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
于是做js库的配置是
-
babel-loader
@8.2.5 -
@babel/core
@7.19.1 -
@babel/preset-env
@7.19.1 -
@babel/plugin-transform-runtime
@7.19.1 -
@babel/runtime-corejs2
@7.19.1
装包
npm install babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/runtime-corejs2 -D
babel.config.js 配置
module.exports = {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage",
corejs: 2,
modules: false,
},
],
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: 2,
},
],
],
};
总结
@babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/polyfill
额外:@babel/cli @babel/runtime-corejs2 @babel-loader webpack webpack-cli
babel作用: ES6语法 -> ES5语法,只转语法,不转api
@babel/preset-env: useBuiltIns: "usage" 自动引入@babel/polyfill需要的api useBuiltIns: "entry" 需要在入口开始位置手动引入@babel/polyfill,而且项目只能引一次,否则会报错 问题:是commonjs,在webpack中tree shaking需要用esm的(import/export),导致tree shaking失效 解决:@babel/preset-env,modules: false,babel用esm语法,交给webpack做tree shaking
@babel/plugin-transform-runtime: 引用@babel/runtime中工具函数 问题:修改window全局对象,在window.Promise = function(),对于web项目没问题,但是如果你是在做分装js库,修改window就有隐患 解决:加参数corejs: 2 并安装 @babel/runtime-corejs2