Babel 社区概览

本文发布于 2019-04-15,总结了 babel 社区的工具使用,以及如何合理地进行配置。若是要看结论的话,直接跳到文章最后一节。javascript

目录:

@babel/preset-env

介绍

babel-preset-env 是一系列插件的合集,官方已不用再使用 preset-201x 和 preset-latst 之类的包,env 包能够根据配置自动处理兼容代码。html

文档:babeljs.io/docs/en/bab…java

targets

string | Array<string> | { [string]: string }, defaults to {}node

针对你的项目指定生成的代码环境。git

能够是字符串:es6

{
  "targets": "> 0.25%, not dead"
}
复制代码

也能够是对象:github

{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}
复制代码

默认是{}chrome

{
  "targets": {}
}
复制代码

其中的环境值能够参考 browserslist 项目。express

targets.esmodulesnpm

boolean

也能够针对那些支持 ES Module 的浏览器而优化。当指定本选项时,browsers 字段会被忽视。你能够和 <script type="module"></script> 一块儿使用,来生成更小的脚本代码。

请注意: 当指定 esmodules 选项, browsers targets 会被忽视。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "esmodules": true
        }
      }
    ]
  ]
}
复制代码

targets.node

string | "current" | true

若是要针对当前 node 版本进行编译,能够指定 "node" :true"node":"current",它与 "node":process.versions.node 相同。

targets.safari

string | "tp"

若是要针对 Safari 的技术预览版进行编译,能够指定“safari”:“tp”。

targets.browsers(废弃)

string | Array<string>

使用 browserslist 选择浏览器的查询(例如:last 2 versions, > 5%, safari tp)。

注意,browsers 的结果会被 targets 中的显式项覆盖。(此特性通过检验已废弃)

注意:这将在更高版本中删除,而不是直接将 targets 设置为 browserslist 的兼容查询。

{
  "targets": {
    "browsers": {
        "chrome": "58",
        "ie": "11"
    }
  }
}
// 等价于,但最新版已经不能用了
{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}
复制代码

spec

boolean, 默认 false

为此预设中支持它们的任何插件启用更符合规范但可能更慢的转换。

loose

boolean, 默认 false

为此预设中容许它们的任何插件启用 "loose" 转换。

modules

"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, defaults to "auto"

启用将 ES6 模块语法转换为其余模块类型。

将此设置为 false 将不会转换模块。

也要注意 cjscommonjs 的别名。

debug

boolean, 默认 false

经过 console.log 输出使用的 targets/plugins 和插件数据中指定的版本信息。

include

Array<string|RegExp>,默认 []

一个老是包含的插件数组。

如下是有效的配置:

  • Babel 插件@babel/plugin-transform-spread 和不带前缀的 plugin-transform-spread 的写法都支持。
  • 内置特性,好比 es6.mapes6.set,或者 es6.object.assign

能够彻底或部分指定插件名称(或使用 RegExp )。

支持的写法:

  • 全称(string): "es6.math.sign"
  • 部分 (string): "es6.math.*" (解析为全部带 es6.math 前缀的插件)
  • 正则对象: /^transform-.*$/ 或者 new RegExp("^transform-modules-.*")

注意,上面的正则对象中的 . 意思是匹配任何字符,而不是实际的 . 字符。 另请注意,匹配任何字符。.* 是在正则中使用,不一样于 *glob格式中使用。

此选项主要针对于原生加强代码中的 BUG,或者一系列没有起做用的不受支持的功能特性。

例如,node 4 支持原生 class 但不支持 spread。若是 super 须要 spread 特性,那么须要包含 @babel/plugin-transform-classes 插件。

注意:includeexclude 选项仅适用于此预设中包含的插件; 所以,在选项中包含 @babel/plugin-proposal-do-expressions 排除或 @babel/plugin-proposal-function-bind 会抛出错误(由于此预设没有这些项目)。要使用此预设中未包含的插件,请直接将其添加到 plugin 选项中。

exclude

Array<string|RegExp>,默认 []

一个老是排除/移除的插件数组。

配置选项与 include 相同。

若是您不想使用 generators 而且不想包含 regeneratorRuntime(使用 useBuiltIns 时),或者使用了其余插件(如 fast-async)而不是 Babel's async-to-gen,则此选项能够将 @babel/plugin-transform-regenerator 等转换禁用。

useBuiltIns

"usage" | "entry" | false, 默认是 false

此选项将 core-js 模块直接引用为裸导入。所以,core-js 将相对于文件自己进行解析,而且是可被访问的。若是没有 core-js 依赖项或者有多个版本,您可能须要将 core-js@2 指定为应用程序中的顶级依赖项。

这个选项配置了 @babel/preset-env 如何处理 polyfills。

useBuiltIns: 'entry'

注意:只须要在你整个 app 中使用 require("@babel/polyfill"); 一次。屡次对 @babel/polyfill 的导入会致使全局冲突和其余很难跟踪的错误。咱们推荐建立一个单独的文件处理 require 语句。

这个选项会启用一个新的插件,将 import "@babel/polyfill" 或者 require("@babel/polyfill") 替换为 @babel/polyfill 下的各个基于不一样环境的单独项导入。

npm install @babel/polyfill --save
复制代码

输入

import "@babel/polyfill";
复制代码

输出(不一样的配置环境下有所区别)

import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
复制代码

也能够直接导入 core-jsimport "core-js"; or require('core-js');

useBuiltIns: 'usage'(实验性)

在每一个文件中使用 polyfill 时,为 polyfill 添加特定导入。咱们利用 bundler 只加载一次相同的 polyfill。

输入

a.js

var a = new Promise();
复制代码

b.js

var b = new Map();
复制代码

输出(若是当前配置环境不支持此特性)

import "core-js/modules/es6.promise";
var a = new Promise();
复制代码
import "core-js/modules/es6.map";
var b = new Map();
复制代码

输出(若是当前配置环境支持此特性)

var a = new Promise();
复制代码
var b = new Map();
复制代码

useBuiltIns: false

既不会在每一个文件中自动添加 polyfill,也不会将 "@babel/polyfill" 导入为单个 polyfill。

简单总结

'usage''entry' 的区别:

  • 'usage' 无需在头部引入 import '@babel/polyfill',它会自动根据当前的代码引入对应特性,而且只引用代码中用到的特性(browserslist 配置 + 代码用到)
  • 'entry' 须要在头部引入 '@babel/polyfill',而且是根据配置环境引入对应的特性。代码中没有用到,但环境中会缺失,也会引入。(只根据 browserslist 配置)

usage 风险项:因为咱们一般会使用不少 npm 的 dependencies 包来进行业务开发,babel 默认是不会检测 依赖包的代码的。 也就是说,若是某个 依赖包使用了 Array.from, 可是本身的业务代码没有使用到该API,构建出来的 polyfill 也不会有 Array.from, 如此一来,可能会在某些使用低版本浏览器的用户出现 BUG。 因此避免这种状况发生,通常开源的第三方库发布上线的时候都是转换成 ES5 的。

corejs

corejs 配置项是决定当前 Babel 使用的版本,有 23 选项。

升级文档中已经说明了,最新版的 Babel7 @babel/polyfill 移除了 polyfill proposals,因此 @babel/polyfill 仅仅是 core-js v2 的别名。

因此这里就须要注意的一点,若是使用 corejs: 2 + useBuiltIns: 'entry' 的话,就会报警告:

`@babel/polyfill` is deprecated. Please, use required parts of `core-js` and `regenerator-runtime/runtime` separately
复制代码

这里须要使用的是 corejs: 3 + useBuiltIns: 'entry',才不会出错。

forceAllTransforms

boolean, 默认 false

因为有了 Babel7 Javascipt config file 的支持,你能够根据是否设置了 production 来控制转换。

module.exports = function(api) {
  return {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: {
            chrome: 59,
            edge: 13,
            firefox: 50,
          },
          // for uglifyjs...
          forceAllTransforms: api.env("production"),
        },
      ],
    ],
  };
};
复制代码

注意: targets.uglify 已被废弃,而且在下一个版本中被移除。

默认状况下,此预设将运行目标环境所需的全部变换。若是你要强制运行全部转换,则启用此选项能够在须要用到 UglifyJS 或仅支持 ES5 语法的某些场景下会颇有用。

configPath

string, 默认是 process.cwd()

决定配置 browserslist 搜索的起点,一直往上到系统根目录,直到找到。

ignoreBrowserslistConfig

boolean, 默认是 false

切换是否使用 browserslist 配置源,包括搜索任何 browserslist 文件或引用package.json 中的 browserslist 键。这对于那些不走 Babel 编译,但使用 browserslist 配置的项目很是有用。

shippedProposals

boolean, 默认是 false

切换启用对浏览器中提供的内置特性的支持。若是你的目标环境对某一个特性提案(proposal)具备原生支持,则会启用与其匹配的解析器语法插件,而不是执行任何转换。请注意,这不会启用与 @babel/preset-stage-3 相同的转换,由于这些提案可能在正式落地浏览器以前会有变动。

目前支持如下内容:

内置:

特性:

@babel/preset-stage-x(废弃)

在 babel7 中,官方已经宣布废弃 babel stage preset 包,大概是考虑到普遍使用的 stage-x 不适合社区的发展,具体缘由见官方博客

在新版的 babel 配置须要根据本身的须要下载对应的 proposal 插件,由于 stage-x 自己也是这些插件的集合,但不包含在 env 包中,好比安装:@babel/plugin-proposal-function-bind,使用这些还没正式进标准但社区已经广为使用的语言特性。

具体使用的话,之前的各个 stage 等同于下面的各个插件的集合:

{
  "plugins": [
    // Stage 0
    "@babel/plugin-proposal-function-bind",

    // Stage 1
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    ["@babel/plugin-proposal-optional-chaining", { "loose": false }],
    ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }],
    ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }],
    "@babel/plugin-proposal-do-expressions",

    // Stage 2
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", { "loose": false }],
    "@babel/plugin-proposal-json-strings"
  ]
}
复制代码

@babel/polyfill

介绍

文档:babeljs.io/docs/en/nex…

官网信息:从 Babel 7.4.0 开始,这个包已经被废弃了,以支持直接导入 core-js/stable(polyfill ECMAScript 特性)和 regenerator-runtime/runtime(须要使用转换后的 generator 函数)

babel-polyfill 的存在乎义是给浏览器“打补丁”,好比浏览器没有 Object.assign 这个特性,它会针对这个环境建立这个特性。Babel 自身是只转换语法,不添加丢失的特性,polyfill 的存在就是弥补浏览器这部分缺失的特性(好比某些 ie)。

babel-polyfill 等同于 regenerator runtime + core-js

  • regenerator:提供对 generator 支持,若是应用代码中用到generator、async函数的话。
  • core-js:提供 es 新的特性。

最新版的具体用法,见 @babel/preset-envuseBuiltIns 特性。

通过个人简单实验,其实能够不用专门安装这个包,并且新的 corejs v3 和 corejs v2 还不太同样(使人困惑)。使用 useBuiltIns` 就好。

反作用

引入 babel-polyfill 也会有必定反作用,好比:

  • 引入了新的全局对象,好比Promise、WeakMap等。
  • 修改现有的全局对象:好比修改了Array、String的原型链等。

在应用开发中,上述行为问题不大,基本可控。但若是在库、工具的开发中引入 babel-polyfill,则会带来潜在的问题。

举个例子,在项目中定义了跟规范不一致的Array.from()函数,同时引入了一个库(依赖 babel-polyfill),此时,这个库可能覆盖了自定义的Array.from()函数,致使出错。

这就是 babel-runtime 存在的缘由。它将开发者依赖的全局内置对象等,抽取成单独的模块,并经过模块导入的方式引入,避免了对全局做用域的修改(污染)。

所以,若是是开发库、工具,能够考虑使用 babel-runtime。

@babel/runtime

介绍

@babel/runtime 是一个包含 Babel modular runtime helpers 和 一系列 regenerator-runtime 的库。

文档:babeljs.io/docs/en/bab…

使用场景

babel-runtime 通常用于两种场景:

  • 开发库/工具
  • 移除冗余工具函数(helper function)。

与 babel-polyfill 的区别在于:

  • babel-polyfill 会修改(覆盖)实例方法,这在业务层颇有用,但某些场景,好比引用外在的技术库,不但愿这里的的 polyfill 覆盖业务代码中的方法。
  • babel-runtime 不会修改实例方法,它只是引入一些 helper 函数,创造对应的方法。

使用 babel-runtime 通常会搭配 babel-plugin-transform-runtime 使用。babel-plugin-transform-runtime 用于构建过程的代码转换,而 babel-runtime 是实际导入项目代码的功能模块。

@babel/plugin-transform-runtime

介绍

文档:babeljs.io/docs/en/bab…

babel 在每一个须要的文件的顶部都会插入一些 helpers 内联代码,这可能会致使多个文件都会有重复的 helpers 代码。@babel/plugin-transform-runtime 的 helpers 选项就能够把这些模块抽离出来。

@babel/plugin-transform-runtime 主要作了三件事情:core-js aliasing、helper aliasing、egenerator aliasing。

  • core-js aliasing:自动导入babel-runtime/core-js,并将全局静态方法、全局内置对象 映射到对应的模块。

  • helper aliasing:将内联的工具函数移除,改为经过babel-runtime/helpers模块进行导入,好比_classCallCheck工具函数。

  • regenerator aliasing:若是你使用了 async/generator 函数,则自动导入 babel-runtime/regenerator模块。

Demo

module.exports = {
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": false, // boolean 或者 number, 默认 false,指定是否须要 runtime 的 corejs aliasing,若是使用 env 的 useBuiltIns + polyfill,使用 false。
                "helpers": true, // boolean, 默认 true,指定是否内联 babel 的 helper 代码 (好比 classCallCheck, extends) 
                "regenerator": false, // 经过 preset-env 已经使用了全局的 regeneratorRuntime, 再也不须要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
                "useESModules": true, // boolean, 默认 false,使用 es modules helpers, 减小 commonJS 语法代码
                "absoluteRuntime": false // boolean, 默认 false,是否目录引用 runtime 包(有些项目会引用当前项目以外的代码,编译时会找不到 runtime 包)
            }
        ]
    ]
}
复制代码

添加新配置前编译出来的代码

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
复制代码

添加新配置后编译出来的代码

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
复制代码

babel-register

babel-register 则提供了动态编译。换句话说,咱们的源代码可以真正运行在生产环境下,不须要 babel 编译这一环节。

咱们先在项目下安装 babel-register

$ npm install --save-dev @babel/register
复制代码

而后在入口文件中 require

require('@babel/register')
require('./app')
复制代码

在入口文件头部引入 @babel/register 后,咱们的 app 文件中便可使用任意 es2015 的特性。

固然,坏处是动态编译,致使程序在速度、性能上有所损耗。因此这一项基本不用在正式的生产环境中使用。

babel-node

上面所说,babel-register 提供动态编译,可以让咱们的源代码真正运行在生产环境下 - 但其实否则,咱们仍须要作部分调整,好比新增一个入口文件,并在该文件中 require('@babel/register')。而 babel-node 能真正作到一行源代码都不须要调整:

$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.js
复制代码

只是,请不要在生产环境中使用 babel-node,由于它是动态编译源代码,应用启动速度很是慢。

一份可用的配置

安装对应的包

依赖:

  • @babel/core(核心包)
  • @babel/preset-env(预设)
  • @babel/polyfill(v7.4.0彷佛被废弃,能够不用安装)
  • core-js(最新版本v3,在配置版本corejs:3的状况下,这个包是用于替代 polyfill 的)
  • @babel/runtime(开发业务代码基本只用到helper配置,开发技术库能够深刻使用)
  • @babel/plugin-transform-runtime(合并重复的 helper 函数)
  • @babel/plugin-proposal-function-bind(没有 stage-x 后,须要安装单独的插件,支持对应的 proposal 特性)

注意:这里 @babel/polyfill 可装可不装,不装彷佛也不影响没有影响,但不肯定正式允运行的时候会不会报错。看了下源码,其实很简单,就是引用到 core-js v2 的特性。官方文档介绍已经被废弃了。

babel.config.js

const presets = [
    [
      "@babel/env",
      {
        targets: {
          edge: "17",
          firefox: "60",
          chrome: "67",
          safari: "11.1",
          ie: '8'
        },
        useBuiltIns: 'usage',
        // Babel7 须要指定引入corejs的版本,最好使用3
        corejs: 3,
        modules: 'amd', // 须要转换成什么样的模块系统
      },
    ],
  ];
  
const plugins = [
    // 帮助减小 helper 函数
    [
      "@babel/plugin-transform-runtime",
      {
          "corejs": false, // 默认值,能够不写
          "helpers": true, // 默认,能够不写
          "regenerator": false, // 经过 preset-env 已经使用了全局的 regeneratorRuntime, 再也不须要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
          "useESModules": true, // 使用 es modules helpers, 减小 commonJS 语法代码
      }
    ],
    // 因为没有了 stage-x,须要单独导入须要的插件
    [
        '@babel/plugin-proposal-function-bind'
    ]
]
module.exports = { presets, plugins };
复制代码

参考

Babel 的使用

Babel 的配置信息

相关文章
相关标签/搜索