我掌握的Babel配置(带视频)

275 阅读8分钟

我正在参加「掘金·启航计划」

视频地址 babel7掌握这四个包就行了

对前端同学来说,Babel是一个需要攻克的痛点。

我们为什么对Babel会疑惑呢?

  • 1、Babel中有几十个包,我们做配置的时候不清楚到底要哪些包

  • 2、网上关于Babel资料很多,同一个功能为什么不同文章用的包不一样

  • 3、按照某些文章列的包安装后,并没有达到效果

我们首先应该明确Babel版本差异大

  • 1、Babel发布时所有包都会发布,版本号都是一致的,即使有些包没有改动一行代码也会发布新版

  • 2、Babel各大版本之间差异很大,目前我们是用Babel7做配置,Babel7包名都是@babel/开头的

  • 2、Babel5Babel6包名不是@开头,这些包就逐渐淘汰了

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、我们看到常用presetsplugins两个配置项,分别叫预设插件,预设就是一些插件的合集,其他配置项minifiedignore等几乎用不到。

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

image.png

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

image.png

图中我们看 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打包出来看效果没有任何变化,目前我还不清楚影响】

比如,如下是禁用了useESModulesclassCallCheck

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