Babel
中文:
Babel 中文网 · Babel - 下一代 JavaScript 语法的编译器
现代浏览器基本都支持了ES6
及更新版本的代码语法,但是不保证所有人都在使用最新的浏览器,这个时候如果我们的项目完全使用最新的语法去编写,到了一些老版本的浏览器就会报错无法执行。
例如我们在src/index.js
文件中写一些ES6
的代码:
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
});
然后运行npm run build
进行打包,打包完成后,我们找到dist/main.js
在这个文件的最后几行会看到这样的代码:
eval("const arr = [new Promise(() => {}), new Promise(() => {})];\n\narr.map(item => {\n console.log(item);\n})//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvaW5kZXguanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9jaGFwdGVyMDEvLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBhcnIgPSBbbmV3IFByb21pc2UoKCkgPT4ge30pLCBuZXcgUHJvbWlzZSgoKSA9PiB7fSldO1xuXG5hcnIubWFwKGl0ZW0gPT4ge1xuICAgIGNvbnNvbGUubG9nKGl0ZW0pO1xufSkiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./src/index.js\n");
这时候会发现**src/index.js**
文件中写的代码未发生改变的被打包了出来,这样的问题就会导致在低版本浏览器中是无法运行代码的(比如**ie5**
)。
babel-loader & @babel/preset-env
这个时候我们就可以利用Babel
将我们写的ES6
语法转换为老版本浏览器可以认识的语法。
安装:
$ npm install babel-loader @babel/preset-env --save-dev
:::info
babel-loader
:使用babel-loader
处理js
文件,实际上babel-loader
是在让webpack
和babel
通信的桥梁,babel-loader
本身并不会将ES6
语法转换成ES5
的语法。@babel/preset-env
:将ES6
语法转换为ES5
的语法,它包含了所有ES6
转为ES5
的语法规则。 :::
配置:
// ...
moudle.exports = {
// ...
module:{
rules:[
// ...
{
test: /\.m?js$/,
// 排除 node_modules 目录下的代码
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
// ...
}
等以后安装好且配置完毕后,再运行npm run build
,就能看到部分代码转为为ES5
的语法了。
eval("var arr = [new Promise(function () {}), new Promise(function () {})];\narr.map(function (item) {\n console.log(item);\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9jaGFwdGVyMDEvLi9zcmMvaW5kZXguanM/YjYzNSJdLCJuYW1lcyI6WyJhcnIiLCJQcm9taXNlIiwibWFwIiwiaXRlbSIsImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUFBLElBQU1BLEdBQUcsR0FBRyxDQUFDLElBQUlDLE9BQUosQ0FBWSxZQUFNLENBQUUsQ0FBcEIsQ0FBRCxFQUF3QixJQUFJQSxPQUFKLENBQVksWUFBTSxDQUFFLENBQXBCLENBQXhCLENBQVo7QUFFQUQsR0FBRyxDQUFDRSxHQUFKLENBQVEsVUFBQUMsSUFBSSxFQUFJO0FBQ1pDLEVBQUFBLE9BQU8sQ0FBQ0MsR0FBUixDQUFZRixJQUFaO0FBQ0gsQ0FGRCIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IGFyciA9IFtuZXcgUHJvbWlzZSgoKSA9PiB7fSksIG5ldyBQcm9taXNlKCgpID0+IHt9KV07XG5cbmFyci5tYXAoaXRlbSA9PiB7XG4gICAgY29uc29sZS5sb2coaXRlbSk7XG59KSJdLCJmaWxlIjoiLi9zcmMvaW5kZXguanMuanMiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./src/index.js\n");
可以明显的看到let
被转换为var
,()=>{}
被转换为function(){}
。
@babel/polyfill
根据上面的dist/main.js
的代码可以看到虽然部分代码被转换为了ES5
的写法,但是像new Promise
,map
这样的方法依然没有进行编译,它们在低版本浏览器依然是不存在的。这时就需要不仅使用@babel/preset-env
来做语法转换,还需要使用@babel/polyfil
来帮我们做代码填充。
:::info
@babel/polyfill
相当于给代码打一个补丁,当我们使用Promise
、Map
这样方法的时候就可以使用这个补丁去实现Promise
等方法。
老版本的浏览器没有新语法的API
,所以我们只能手写实现。但是网上搜到的写法不够权威标准,而且每当一个方法不存在的时候都需要我们手写那就造成很大的开发量而且麻烦,所以我们就希望有一个库既能实现缺失的方法又比较权威标准,而这个就是corejs
库。corejs
库里面就有Promise
这样的方法实现,我们只要使用了它就可以放心的编写ES6+
的代码了,但是corejs
无法转换generator
语法,这个时候🈶又可以使用regenerator
这个库来弥补corejs
的缺陷。
当我们把corejs
和regenerator
结合之后,我们所有的新语法就就都能支持了,然而这就是@babel/polyfill
,它就是进行了汇总。
:::
安装:
$ npm install @babel/polyfill --save-dev
然后在我们需要运行的ES6
的代码文件中的最上面引入@babel/polyfill
来帮我们填充代码:
import "@babel/polyfill";
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
})
运行之后我们会发现打包后的main.js
文件竟有952KiB
???
这是因为我们引入@babel/polyfil
的时候,它把所有的语法全部填充到了代码中,所以我们需要配置Babel
来实现自动按需引入。
// ...
modules.export = {
// ...
module: {
rules: [
// ...
{
test: /\.m?js$/,
// 排除 node_modules 下的代码
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
// presets[ [需要使用 presets 的名字, {具体配置}] ]
presets: [
[
'@babel/preset-env',
{
// 当使用@babel/polyfill填充代码时,按需引入
useBuiltIns: "usage",
// 声明corejs版本(不然无法编译)
corejs: "3"
}
]
]
}
}
}]
},
// ...
}
另外还需要删除src/index.js
中的@babel/polyfil
完整引入(否则按需引入就配置了个寂寞)。
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
})
最后运行nm run build
进行打包,bundle.js
成功了缩减到了146KiB
。
当我们想开发一个类库或者UI
组件库的时候,@babel/polyfill
就不适用了,因为这是@babel/polyfill
会污染全局的变量名。
例如@babel/polyfill
内实现Promise
是全局挂载到window
对象上的,window.Promise = function(){...}
,假如我们用的其他代码库也挂载了一个widnow.Promise
属性,这样就会产生冲突!!!
可以使用@babel/plugin-transform-runtime
来解决这个问题。
@babel/plugin-transform-runtime · Babel 中文网
因为它会有效的避免全局污染的问题,因为它是以闭包的方式引入。
单独配置 Babel
我们还可以将Babel
的配置单独文件进行管理配置(和webpack.config.js
一样,不能进行随意命名)。
module.exports = {
// 把 babel-loader 中的配置拿到这里
presets: [
[
'@babel/preset-env', {
corejs: "3", // 声明corejs版本
useBuiltIns: "usage" // 当使用@babel/polyfill填充代码时,按需填充。
}
]
]
}
// ...
modules.export = {
// ...
module: {
rules: [
// ...
{
test: /\.m?js$/,
// 排除 node_modules 下的代码
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}]
},
// ...
}