最近在作一次Babel6升级Babel7的操做,把升级的过程和关于babel的配置进行一次总结。前端
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便可以运行在当前和旧版本的浏览器或其余环境中。node
其实目前前端开发,各类项目模版,你也不须要关心babel的配置,随便拉下来一个就能运行,可是要作定制化的处理仍是要把babel搞懂。android
@babel/cli
是Babel的命令行工具,咱们通常用不到,由于咱们一般都是用babel-loader
,里边使用的是@babel/core
的api形式,咱们只须要关心Babel的配置,若是有须要在编译阶段对代码进行处理 也能够写本身的插件,可是大部分场景是须要咱们把Babel的配置搞清楚。ios
Babel6的阶段 最经常使用的是.babelrc
,可是如今Babel7支持了更多格式:git
const RELATIVE_CONFIG_FILENAMES = [".babelrc", ".babelrc.js", ".babelrc.cjs", ".babelrc.mjs", ".babelrc.json"];
和package.json
files with a "babel"
key。github
配置文件的格式以下:web
{ "presets": [ [ "@babel/preset-env", { "modules": "commonjs" } ] ], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ], "@babel/plugin-syntax-dynamic-import", ] } }
更详细介绍参见Babel Config。chrome
plugins
和preset
配置文件中主要有两个配置plugins
和preset
,@babel/core
自己对代码不作任何的转化,可是提供了对代码的解析,各类插件在解析过程当中能够进行代码的转换,好比处理箭头函数的插件@babel/plugin-transform-arrow-functions
等等,因此好比针对ES6语法的解析就须要不少插件,preset
预设就是配置的子集,预设的一套配置,能够根据参数动态的返回配置。npm
顺序问题很重要,好比某一个插件是添加'use strict', 一个插件是删除'use strict',若是想要删除成功,就要保证执行顺序。
在一个配置里面json
因此在preset中的插件,确定比外层的插件要后执行。
plugins
和preset
的配置是数组的形式,若是不须要传参数,最基本的就是字符串名称,若是须要传参数,把它写成数组的形式,数组第一项是字符串名称,第二项是要传的参数对象。
@babel/preset-env已经彻底能够替换
babel-preset-es2015
babel-preset-es2016
babel-preset-es2017
babel-preset-latest
全部stage的preset在Babel v7.0.0-beta.55版本都已经被废弃了,
stage-x:指处于某一阶段的js语言提案
最开始stage的出现是为了方便开发人员,每一个阶段的插件与TC39和社区相互做用,同步更新,用户能够直接引用对应stage支持的语法特性。关于废弃的缘由 总结下来是:
先说下已经有了Babel为何还要polyfill,Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,好比 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(好比Object.assign)都不会转码。举个栗子,ES6在Array对象上新增了Array.from方法。babel就不会转码这个方法。因此以前咱们都须要引入polyfill。
可是从Babel 7.4.0开始,不推荐使用此软件包,而直接包括core-js/stable
(包括regenerator-runtime/runtime
polyfill ECMAScript功能)和(须要使用转译的生成器函数)。
import "core-js/stable"; import "regenerator-runtime/runtime";
可是最优的方式也不是直接这样引入,后面讲@babel/preset-env
的使用时会有更好的方式。
关于升级,官方提供了工具 babel-upgrade
总结关键点以下:
npx babel-upgrade --write --install
,两个参数,--write
会把更新的配置写入babel的配置文件中,package.json
中也会更新依赖,可是发现没有的依赖没有新增,因此我在更新的时候把配置中依赖的npm包,在package.json
都check了一遍。--install
是会进行一次安装操做。@babel/preset-env
是Babel推荐的最智能的预设,在使用了 babel-upgrade
升级以后你就能够看到配置中会有这个预设,由于设个预设集成了经常使用插件和polyfill能力,能够根据用户指定的环境寻找对应的插件。
下面对它的关键配置项作说明。
string | Array<string> | { [string]: string }
,默认为{}
。
描述您为项目支持/目标的环境。
这能够是与浏览器列表兼容的查询:
`{ "targets": "> 0.25%, not dead" }`
或支持最低环境版本的对象:
`{ "targets": { "chrome": "58", "ie": "11" } }`
实施例的环境中:chrome
,opera
,edge
,firefox
,safari
,ie
,ios
,android
,node
,electron
。
若是未指定目标,则旁注@babel/preset-env
将默认转换全部ECMAScript 2015+代码,因此不建议。
"usage"
| "entry"
| false
,默认为false
。
此选项决定@babel/preset-env
如何处理polyfill的引入。
前面将废弃polyfill
时 讲到了polyfill如今分为两个npm包,是这样引入
import "core-js/stable"; import "regenerator-runtime/runtime";
可是问题是全量引入,增长包体积,因此useBuiltIns选项就是对其进行优化。
当取值"entry"
时,@babel/preset-env
会把全量引入替换为目标环境特定须要的模块。
当目标浏览器是 chrome 72
时,上面的内容将被 @babel/preset-env
转换为
require("core-js/modules/es.array.reduce"); require("core-js/modules/es.array.reduce-right"); require("core-js/modules/es.array.unscopables.flat"); require("core-js/modules/es.array.unscopables.flat-map"); require("core-js/modules/es.math.hypot"); require("core-js/modules/es.object.from-entries"); require("core-js/modules/web.immediate");
当取值"usage"
时,咱们无需手动引入polyfill
文件,@babel/preset-env
在每一个文件的开头引入目标环境不支持、仅在当前文件中使用的 polyfills。
例如,
const set = new Set([1, 2, 3]); [1, 2, 3].includes(2);
当目标环境是老的浏览器例如 ie 11
,将转换为
import "core-js/modules/es.array.includes"; import "core-js/modules/es.array.iterator"; import "core-js/modules/es.object.to-string"; import "core-js/modules/es.set"; const set = new Set([1, 2, 3]); [1, 2, 3].includes(2);
当目标是 chrome 72
时不须要导入,由于这个环境不须要 polyfills:
const set = new Set([1, 2, 3]); [1, 2, 3].includes(2);
core-js就是Javascript标准库的polyfill,@babel/preset-env
的polyfill就依赖于它,因此咱们须要指定使用的core-js的版本,目前最新版本是3。
默认状况下,仅注入稳定ECMAScript功能的polyfill,若是想使用一些提案的语法,能够有三种选择:
useBuiltIns: "entry"
时,能够直接导入建议填充工具:import "core-js/proposals/string-replace-all"
。使用useBuiltIns: "usage"
时,您有两种不一样的选择:
shippedProposals
选项设置为true
。这将启用已经在浏览器中发布一段时间的投标的polyfill和transforms。corejs: { version: 3, proposals: true }
。这样能够对所支持的每一个提案进行填充core-js
。我以为这个选择有用,由于@babel/preset-env
中内置的插件,咱们没法在其后执行,好比里面内置的"@babel/plugin-transform-modules-commonjs"
插件会默认的在全部的模块上都添加use strict
严格模式, 虽然有babel-plugin-remove-use-strict
用于移除use strict
可是因为执行顺序的问题,仍是没法移除。
第二个问题就是内置插件没法传参数的问题。
因此我想到的方法是先exclude排除掉这个插件,而后在外层再添加 这样就能够改变执行顺序同时也能够自定义传参数。
已经有了polyfill,这个包的做用是什么?主要分两类:
await
的_asyncToGenerator
和asyncGeneratorStep
,使用了它以后会把这些方法经过@babel/runtime/helpers
中的模块进行替换。例如代码
async function a () { await new Promise(function(resolve, reject) { resolve(1) }) }
没使用以前,编译结果
require("regenerator-runtime/runtime"); function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } 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); }); }; } function _a() { _a = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return new Promise(function (resolve, reject) { resolve(1); }); case 2: case "end": return _context.stop(); } } }, _callee); })); return _a.apply(this, arguments); }
使用以后
var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs3/regenerator")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); require("regenerator-runtime/runtime"); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/asyncToGenerator"));
@babel/preset-env
中引入的polyfill都是直接引入的core-js
下的模块,它的问题会污染全局变量,好比
"foobar".includes("foo");
编译后的polyfill是给String.prototype
添加了includes方法,因此会影响全局的String
对象。
require("core-js/modules/es.string.includes");
而使用了@babel/plugin-transform-runtime
后的编译结果
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes")); (0, _includes.default)(_context = "foobar").call(_context, "foo");
会把代码中用的到的方法进行包装,而不会对全局变量产生影响。
最后是 @babel/plugin-transform-runtime
的配置项,关键的是指定 core-js
的版本。
corejs: 2
仅支持全局变量(例如Promise
)和静态属性(例如Array.from
),corejs: 3
还支持实例属性(例如[].includes
)。
默认状况下,@babel/plugin-transform-runtime
不填充提案。若是您使用corejs: 3
,则能够经过使用proposals: true
选项启用此功能。
须要安装对应的运行时依赖:npm install --save @babel/runtime-corejs3
最后 你能够基于以上知识已经建立了符合本身团队开发的preset。
若是以为有收获请关注微信公众号 前端良文 每周都会分享前端开发中的干货知识点。