blogs
blogs copied to clipboard
babel7的配置与优化。
网上关于babel7的文章很多,但是大多都没有实践,很多讲的模棱两可。 本文将手把手的带你看各种配置下的输入输出转换,彻底让你了解babel7到底该怎么去配置和优化。
首先我们知道进入了babel7的时代,stage-0这种已经作为不推荐使用的present了,最流行的应该是@babel/present-env 顾名思义让babel拥有根据你的环境来编译不同代码的需求。
targets
我们先配置最基础的.babelrc配置
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "58",
"ie": "10"
}
},
]
],
}
targets配置的意思就是让babel根据你写入的兼容平台来做代码转换,这里我们指定ie10为我们要兼容的最低版本,来看下面es6代码的输出。
输入: src/main.js
const a = () => {}
输出: dist/main.js
var a = function a() {};
这里因为ie10是不支持es6语法的,所以代码被全部转换,如果我们把ie10这条去掉,因为高版本的chrome是支持es6大部分语法的,所以代码就不会被做任何转换了。
browserlist 这里是具体的可配置列表,可以根据你自己项目的兼容性要求来配置。
useBuiltIns
首先我们来看一行简单的代码
a.includes(1);
includes作为数组的实例方法,在某些浏览器其实是不支持的,babel默认的转换对于这种场景并不会做处理,同样不会处理的包括WeakMap, WeakSet, Promise等es6新引入的类,所以我们需要babel-polyfill为我们这些实例方法等等打上补丁。
在很多项目中我们会看到项目的main.js入口顶部require了babel-polyfill包, 或者指定webpack的entry为数组,第一项引入babel-polyfill包,这样的确没问题而且很保险,但是很多场景下我们可能只是使用了少量需要polyfill的api,这个时候全量引入这个包就显得很不划算,babel给我们提供了很好的解决方案,那就是useBuiltIns 这个配置,下面来看实例。
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"targets": {
"chrome": "58",
"ie": "10"
}
},
]
],
}
输入: src/main.js
a.includes(1)
Promise.reject()
输出: dist/main.js
require("core-js/modules/es6.promise");
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.string.includes");
a.includes(1);
Promise.reject();
babel帮我们做好了代码分析,在需要用到polyfill的地方再帮你引入这个单独的补丁,这样就实现了按需引入~
@babel/plugin-transform-runtime
这个插件是帮我们把一些babel的辅助方法由直接写入代码专为按需引入模块的方式引用, 我们先来看不使用这个插件时候,我们对于es6 class的转换。
输入: src/main.js
class A {}
输出: dist/main.js
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);
};
看似没问题,转换的很好,但是如果在很多模块都用了class语法的情况下呢?辅助函数_classCallCheck就会被重复写入多次,占用无意义的空间。 解决方法就是引入@babel/plugin-transform-runtime .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"targets": {
"chrome": "58",
"ie": "10"
}
},
]
],
"plugins": [
"@babel/plugin-transform-runtime",
]
}
输入: src/main.js
class A {}
输出: dist/main.js
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var A = function A() {
(0, _classCallCheck2.default)(this, A);
};
这样就解决了辅助函数重复写入的问题了。
总结
babel7的版本下,利用present-env做按需转换,利用useBuiltIn做babel-polyfill的按需引入,利用transform-runtime做babel辅助函数的按需引入。
useBuiltIns 的usage 和 entry 有什么区别???
usage 看明白了 entry ,貌似要先引入@babel/polyfill ,然后内部进行按需引入,是这样吗 应该还要配置一个corejs 版本,不然有些最新的特性语法,无法编译出来。像flat
@webgzh907247189 使用 entry 的配置项的话需要在代码里面手动引入 @babel/polyfill ,这样通过babel 编译后的代码会将所有的 polyfill 都引入到你的编译后的代码里面去。这里也可以参考下[email protected]的文档里面有关 babel 的 useBuiltIns 的说明
@CommanderXL 感谢你的回答。。大佬能否看下https://github.com/webgzh907247189/designPattern/blob/master/babel/index.js 为何我测试用了require('@babel/polyfill') 但是最终输出没有引用。十分感谢
@webgzh907247189 额,你是在node环境下去运行代码的。@babel/polyfill里面涉及到的东西没必要输入到你的代码里面了。直接作为一个package来使用就好了。
一般在web环境下,代码需要编译,肯定不能在本地直接引入package,那么这个时候就需要把@babel/polyfill所提供的polyfills打包到最后需要编译输出的代码。
可以见我写的demo
想问一下,用了@babel/plugin-transform-runtime和@babel/runtime,在@babel/preset-env中还需要配置useBuiltIns和corejs吗。。 我整的一脸懵逼,没有做到提取代码,减少代码体积。
@tulies 这个得看你具体运用的场景了。比如你是使用babel编译一个工程应用项目,用 @babel/preset-env + useBuiltIns: 'entry' + browserlist 的方式基本上就可以 cover 住你项目的代码所运行的场景了。但是如果你是需要使用 babel 去构建一个 library 的话,就用 @babel/plugin-transform-runtime 这种方式。这块具体的内容可以参加 babel 的官方文档上的说明。