在我们的项目中,如果使用了es6的语法和API时就要用到babel对这些语法进行转化,使代码可以在低版本的浏览器上正常运行。
假如有一段es6代码(main.js):
async function f() {
return await 123;
}
f().then(v => console.log(v))
console.log(Object.values({ 1: 2 }));
console.log(Array.isArray([]));
console.log([1, 2, 3].includes(3));
package.json
{
"name": "babel-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --config webpack.config.js",
"dev": "webpack-dev-server --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.8.0",
"babel-loader": "^7.1.4",
"css-loader": "^1.0.1",
"regenerator-runtime": "^0.13.2",
"webpack": "^4.34.0",
"webpack-cli": "^3.3.4"
},
"dependencies": {
"@babel/polyfill": "^7.4.4",
"@babel/runtime": "^7.4.5",
"@babel/runtime-corejs2": "^7.4.5"
}
}
webpack4的配置:
const path = require('path');
module.exports = {
mode: "development",
devtool: "inline-cheap-module-source-map",
entry: {
app: ['./main.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader']
}
]
}
};
注释掉rules,打包后在IE浏览器运行报错
释放掉rules,增加.babelrc文件,配置如下:{
"presets": [["@babel/preset-env"]],
}
因为单独使用babel-loader是不能对es6的语法进行解析的,需要配合babel-preset-env。 继续打包运行
依然报错,但是报错信息发生了变化,因为babel-loader只会对es6的语法进行解析,例如箭头函数。如果要解析es6的API方法就要引入babel-polyfill或者babel-runtime。
babel-polyfill
模拟一个完整的es2015+环境,用于应用程序的开发而不是库文件,可以使用Promise之类的新的内置组件和Array.from和Object.assign之类的静态方法和 Array.prototype.includes等实例方法,polyfill将添加到全局范围和本地原型中,因此会污染全局变量。
引入babel-polyfill的四种方式:
- 直接在main.js顶部使用import "@babel/polyfill"
- 设置.babelrc
{
"presets": [["@babel/preset-env", {"useBuiltIns": "entry", "corejs": 2}]],
}
并在main.js顶部使用import "@babel/polyfill"
- 设置.babelrc
{
"presets": [["@babel/preset-env", {"useBuiltIns": false}]],
}
在webpack.config.js中入口配置:
entry: {
app: ['@babel/polyfill', './main.js']
},
4.设置.babelrc,无需引用@babel/polyfill
{
"presets": [["@babel/preset-env", {"useBuiltIns": "usage", "corejs": 2}]],
}
该配置会自动加载项目中需要的polyfill,而不是全部引入。
打包之后bundle.js大小的比较:
打包之前:
使用前三种引用方式:
使用第四种引用方式:
可见前三种属于全部引用,第四种属于按需引用。当然也可以从安装包里找到所需的垫片手动导入。
babel-runtime
项目中安装依赖包:
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
babel-runtime为生产依赖,transform-runtime为开发依赖,从这里即可看出,babel-runtime中包含了所有的核心帮助函数。使用babel-runtime的两种方式
- 在项目文件中手动引入所需要的帮助函数,例如
import Promise from "babel-runtime/core-js/promise";
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
项目中只使用了Promise,所以只需要引入Promise的帮助函数即可。查看打包之后的bundle.js,可以看到Promise被重写,而不是和babel-polyfill一样放在global上。但是当项目文件比较多,使用的es6 API也比较多时,手动引入就变的比较繁琐。这时就需要transform-runtime插件了,该插件会分析项目代码,自动的引入所需的垫片APIs。使用方法:在.babelrc中配置:
{
"plugins": [["@babel/plugin-transform-runtime", {"corejs": 2}]]
}
并安装依赖:
npm install --save @babel/runtime-corejs2
去掉手动引入的core-js/promise文件,打包。可以看到打包完之后的文件和手动引入的文件大小相同,并且都可以正常在IE中运行。transform-runtime的配置参数corejs默认值为false,假定用户将引入所有需要使用的帮助文件,所以为了达到自动引入的效果需要手动的改为2.否则不会解析es6的代码。
babel-runtime为你的代码提供了一个沙盒环境,所以不会像babel-polyfill一样污染全局变量,因此适用于开发组件库。但是babel-runtime不能模拟实例方法,即内置对象原型上的方法,例如Array.prototype.concat。
但是如果babel版本>=7.4.0,设置corejs: 3。同样安装依赖
npm install --save @babel/runtime-corejs3
在IE中[1, 2, 3].includes(3)方法可以正常执行。可见core: 3在core: 2的基础上解决了babel-runtime不能解析实例方法的弱点。