平时在开发的过程当中,咱们可能并不太须要十分了解babel的内容,仅仅知道它可以将新特性的代码转换成可以在旧版本浏览器中运行的代码。可是这一次想要趁着本身搭建脚手架的机会去进一步的了解babel的知识,因此写了这篇文章。如下内容是babel 7.4以后的版本,也就是@babel/polyfill
被废弃须要独立安装core-js
和 regenerator-runtime
模块的版本。npm
@babel/cli
是babel的命令行工具,主要提供babel
命令。另外还须要安装@babel/core
才能使用babel去编译。json
npm install --save-dev @babel/core @babel/cli
将命令配置在 package.json 文件的 scripts 字段中:promise
// package.json "scripts": { "compiler": "babel src --out-dir lib --watch" }
这样就可以经过npm run compiler
来执行编译,可是babel自己什么都不作,须要添加插件来帮助babel完成工做。浏览器
babel全部功能都创建在各类的plugin上,使用方式是安装相应的plugin
再去配置文件中去使用。例如箭头函数转换插件,
安装@babel/plugin-transform-arrow-functions
,而后在.babelrc
配置文件中去指定对应的插件babel
//.babelrc { plugins: ["@babel/plugin-transform-arrow-functions"], };
而后执行npm run compiler
,能够看到箭头函数已经被编译完成async
可是若是咱们每一个功能都去一个个添加对应的plugin
会很麻烦,多以咱们就须要preset
预设去直接添加一组插件。函数
preset
就是一组插件的集合,最经常使用的preset
就是@babel/preset-env
。工具
它的做用是根据目标环境去进行语法转换和导入对应的polyfill
。post
须要注意的是,@babel/preset-env
会根据你配置的目标环境,生成插件列表来编译。默认状况下,若是你没有在 Babel 配置文件中(如 .babelrc)设置 targets 或 ignoreBrowserslistConfig,@babel/preset-env
会使用 package.json
的browserslist
配置源。ui
咱们能够模拟生产环境和开发环境的浏览器版本
const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"];
经过设置不一样浏览器环境使用@babel/preset-env
去编译相同代码,能够看到最终的结果也会不一样。
module.exports = { presets: [ [ "@babel/preset-env", { // targets: product, targets: development, }, ], ], };
babel 只负责对语法进行编译,好比当咱们写箭头函数,babel 会帮你把它编译成普通函数。可是对一些新的扩展方法,新的类来讲babel就不能转换了。这时就须要去引入polyfill
,polyfill
的中文意思是垫片,所谓垫片就是垫平不一样浏览器或者不一样环境下的差别,让新的内置函数、实例方法等在低版本浏览器中也可使用。
babel v7.4版以后,须要直接安装core-js
和 regenerator-runtime
去替代以前的@babel/polyfill
。croe-js
提供了 ES五、ES6 规范中新定义的各类对象、方法的polyfill,regenerator-runtime
用来实现 ES6/ES7 中 generators、yield、async 及 await 等相关的 polyfill。
首先,咱们须要安装他们到生产环境中,由于须要在生产环境中运行其中的polyfill
。
npm install --save core-js regenerator-runtime
在@babel/preset-env
的配置项中把useBuiltIns
设置成usage
,这样会根据目标浏览器环境去引入所须要的polyfill
。须要注意点是,设置useBuiltIns
还须要同时设置corejs
。
//.babelrc const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: development, useBuiltIns: "usage", corejs: 3, }, ], ], };
//index.js const isHas = [1, 2, 3].includes(2); const getData = () => new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 1000); }); const main = async () => { const res = await getData(); console.log(res); }; main();
编译后的文件:
能够看到,编译后的文件中只引入了所用到的polyfill。
useBuiltIns
还能够设置成其余值,好比entry
,这须要在项目入口文件手动引入polyfills,例如@babel/polyfill
或者core-js
//.babelrc module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "entry", corejs: 3, }, ], ], }; //index.js // 入口文件引入core-js require("core-js");
可是这种方式会引入全量的polyfill。
useBuiltIns
默认值为false
,表明每一个文件里不自动添加polyfill,或不将import "@babel/polyfill"
转换为单独的polyfill。
在使用@babel/preset-env
配合useBuiltIns: usage
时,文件中会引入一些辅助方法例如_classCallCheck
,当多处文件都使用到class时一样也会在每一个文件中去引入这些辅助方法,这样会增大打包体积而且彻底没有必要屡次去引入一样的辅助方法。
//index.js class A {} //.babelrc.js const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], };
编译结果:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var A = function A() { _classCallCheck(this, A); };
为了解决这个问题就须要使用@babel/plugin-transform-runtime
,使用该插件,全部辅助方法都将引用模块 @babel/runtime
,这样就能够避免编译后的代码中出现重复的辅助方法,有效减小包体积。
@babel/plugin-transform-runtime
须要配合@babel/runtime
来使用,@babel/plugin-transform-runtime
在开发时使用,最终代码须要依赖@babel/runtime
。
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime
//index.js class A {} //.babelrc.js const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], //使用@babel/plugin-transform-runtime plugins: [["@babel/plugin-transform-runtime"]], };
编译结果:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var A = function A() { (0, _classCallCheck2.default)(this, A); };
能够看到这些辅助方法都是从@babel/runtime
中引入。
以前在使用@babel/preset-env
编译promise和includes时会引入core-js
中的全局变量或者在对应的原型链中添加相应的方法,这样都形成了全局环境的污染。虽然这对于应用程序或命令行工具是能够的,可是若是你的代码是要发布供他人使用的库,或者没法彻底控制代码运行的环境,则将成为一个问题。
首先,单独使用@babel/plugin-transform-runtime
只可以处理辅助方法,若是想要去引入polyfill
就须要配合@babel/runtime-corejs3
使用。
一样仍是在生产环境安装@babel/runtime-corejs3
。
npm install @babel/runtime-corejs3 --save
这里须要在.babelrc
中去除@babel/preset-env
配置中关于polyfill
的部分以避免与@babel/runtime-corejs3
重复。
//index.js const isHas = [1, 2, 3].includes(2); const getData = () => new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 1000); }); getData(); //.babelrc.js module.exports = { presets: [["@babel/preset-env"]], plugins: [ [ "@babel/plugin-transform-runtime", { corejs: 3, }, ], ], };
编译结果:
"use strict"; var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault"); var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes")); var _context; var isHas = (0, _includes["default"])(_context = [1, 2, 3]).call(_context, 2); var getData = function getData() { return new _promise["default"](function (resolve, reject) { (0, _setTimeout2["default"])(function () { resolve(100); }, 1000); }); }; getData();
能够看到,使用@babel/plugin-transform-runtime
会用一个临时变量去保存polyfill中的一些值,并非直接去修改原型链或者新增Promise方法。
@babel/preset-env
配合useBuiltIns: usage
,在开发第三方库时使用@babel/plugin-transform-runtime
在上面介绍@babel/plugin-transform-runtime
的一些使用时能够看到,它不只可以处理引入屡次helper辅助方法的问题,并且在只引入所需polyfill时还不会污染全局环境,那还有必要使用@babel/preset-env
的useBuiltIns
吗?
其实@babel/plugin-transform-runtime
配合@babel/runtime-corejs3
引入polyfill有一个很大的不足就是不可以经过设置目标环境去引入所须要的polyfil。,咱们在普通开发时只须要在package.json
中的browserslist
去设置开发环境和生产环境的浏览器版本,而后经过使用@babel/preset-env
的useBuiltIns
就可以根据不一样的运行环境去引入适当的polyfill。
可是在开发第三方库时,不能肯定代码的运行环境,因此就须要利用@babel/plugin-transform-runtime
来保证引入的polyfill不去污染全局环境。
通常开发: 经过useBuiltIns: usage
去保证引入恰当的polyfill,经过@babel/plugin-transform-runtime
保证辅助函数都是引用@babel/runtime
`。
const product = ["ie >= 9"]; const development = ["last 2 Chrome versions"]; module.exports = { presets: [ [ "@babel/preset-env", { //代替browserslist设置浏览器版本 targets: product, useBuiltIns: "usage", corejs: 3, }, ], ], plugins: [["@babel/plugin-transform-runtime"]], };