在项目开发过程当中,咱们每每须要引入babel来解决代码兼容性的问题。目前有三种方式,分别是
babel-polyfill,babel-runtime和babel-preset-env
,那么这三种方式有什么区别,结合webpack打包出来的效果哪一种比较优呢,下面咱们来对比一下。css
开始对比以前,咱们须要初始化一个webpack项目node
npm init babel-test
复制代码
这里咱们不打算安装webpack 4.X的版本,安装一下3.X的版本便可webpack
npm i -D webpack@3.7.0
复制代码
在项目根目录下新建一个webpack.config.js文件,配置以下git
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// JavaScript 执行入口文件
entry: {
app: ['./main.js']
},
output: {
// 把全部依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
devServer: {
contentBase: path.join(__dirname, "dist"),
inline: true
},
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
// use: ['style-loader', 'css-loader?minimize'],
use: ExtractTextPlugin.extract({
// 转换 .css 文件须要使用的 Loader
use: ['css-loader']
})
},
{
test: /\.js$/,
use: ['babel-loader']
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 .js 文件中提取出来的 .css 文件的名称
filename: `[name].css`,
})
]
};
复制代码
package.json配置以下es6
{
"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": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.11",
"extract-text-webpack-plugin": "^3.0.2",
"style-loader": "^0.20.3",
"webpack": "^3.7.0",
"webpack-cli": "^1.5.3",
"webpack-dev-server": "^2.11.1"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0"
}
}
复制代码
其中涉及到的包执行一下npm install
安装一下便可,这里就不赘述了。这里的部分包接下来会重复提到,说明为何要这样装github
项目初始化完成后的项目结构以下web
咱们往main.js里面写入一些代码chrome
const elements = [1, 2, 3].map((item) => {
return (
console.log('9999')
)
});
console.log(elements);
async function azumia() {
console.log('begin');
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000)
})
console.log('done');
}
azumia();
console.log(Object.values({ 1: 2 }));
console.log(Array.isArray([]));
复制代码
babel-polyfill 是为了模拟一个完整的ES2015+环境,旨在用于应用程序而不是库/工具。而且使用babel-node时,这个polyfill会自动加载。这里要注意的是babel-polyfill是一次性引入你的项目中的,而且同项目代码一块儿编译到生产环境。并且会污染全局变量。像Map,Array.prototype.find这些就存在于全局空间中。npm
因此这里将其安装到生产环境json
npm install babel-polyfill --save
复制代码
webpack.config.js中这样配置便可
entry: {
app: ['babel-polyfill','./main.js']
}
复制代码
咱们执行一下一下命令,开始打包
npm run start
复制代码
打包出来的bundle.js的文件大小为259K
,而加入babel-polyfill以前的包的大小仅为4K
,体积大了许多。 那么咱们能不能作到按需引用babel-polyfill,从而减少包的大小呢?答案是能够的,这就要靠babel-runtime来实现了。
babel-runtime不会污染全局空间和内置对象原型。事实上babel-runtime是一个模块,你能够把它做为依赖来达成ES2015的支持。
好比环境不支持Promise,你能够在项目中加入
require(‘babel-runtime/core-js/promise’)
复制代码
来获取Promise。
这样咱们就弥补了babel-polyfill的缺点,达到了按需加载的效果。可是在实际项目开发过程当中,咱们每每会写不少新的es6 api,每次都要手动引入相应的包比较麻烦,维护起来也不方便,每一个文件重复引入也形成代码的臃肿。
要解决这个问题,就要用到 babel-plugin-transform-runtime
,它会分析咱们的 ast 中,是否有引用 babel-rumtime 中的垫片(经过映射关系),若是有,就会在当前模块顶部插入咱们须要的垫片。
接下来咱们尝试一下,先安装babel-runtime和babel-plugin-transform-runtime
npm install --save babel-runtime
npm install --save-dev babel-plugin-transform-runtime
复制代码
因为 babel-runtime只是集中了polyfill的library,对应须要的 polyfill 都是要引入项目中,并跟项目代码一块儿打包的,因此就要加入到生产环境依赖中
下面在.babelrc中加入如下配置
{
"plugins": ["transform-runtime"]
}
复制代码
执行打包命令,打包出来的bundle.js的大小为63K
,比完整引入polyfill小了好多。可是事物都有两面性,babel-runtime有个缺点,它不模拟实例方法
,即内置对象原型上的方法,因此相似Array.prototype.find,你经过babel-runtime是没法使用的,这只能经过 babel-polyfill 来转码,由于 babel-polyfill 是直接在原型链上增长方法。这就悲催了,难道仍是要完整引入babel-polyfill?其实还有一个解决的办法,就是用babel-preset-env
babel-preset-env 能根据当前的运行环境,自动肯定你须要的 plugins 和 polyfills。经过各个 es标准 feature 在不一样浏览器以及 node 版本的支持状况,再去维护一个 feature 跟 plugins 之间的映射关系,最终肯定须要的 plugins。关于详细的配置说明,请点击这里
咱们修改一下.babelrc的配置
{
"presets": [
["env", {
"targets": {
"chrome": 52,
"browsers": ["last 2 versions", "safari 7"]
},
"modules": false,
"useBuiltIns": "usage",
"debug": false
}]
]
}
复制代码
其中的useBuiltIns就是是否开启自动支持 polyfill,它能自动给每一个文件添加其须要的poly-fill。 咱们来尝试一下,main.js中映入babel-polyfill
require('babel-polyfill')
复制代码
你没看错,是要在main.js中引入,直接在webpack中配置貌似是不行的。
执行打包命令后,bundle.js的大小为194K
,对比之下仍是要小了些的。
若是此时在浏览器中打开页面,会发现以下报错
这个问题的缘由及解决方案请点击这里,咱们修改一下babel-polyfill的引入方式,require 改成 import,将引入提高到前面
- require('babel-polyfill')
+ import 'babel-polyfill'
复制代码
再次打包后便可
对比以上三种方案,咱们得出如下结论
方案 | 打包后大小 | 优势 | 缺点 |
---|---|---|---|
babel-polyfill | 259K | 完整模拟ES2015+环境 | 体积过大;污染全局对象和内置的对象原型 |
babel-runtime | 63K | 按需引入,打包体积小 | 不模拟实例方法 |
babel-preset-env(开启useBuiltIns) | 194K | 按需引入,可配置性高 | - |
方案没有绝对的优劣,在开发过程当中仍是要根据实际状况灵活运用。