webpack babel-loader 基础配置
Babel
- ES6对JavaScript做了大量改造,加入了大量的方法扩展和suger,让JS灵活性和应用型变的更强,同时写起来也更爽。
- 但是由于ES6 2014年才正式发布,并且加入了大量扩展,造成很多旧版浏览器不能很好的支持ES6的语法及扩展(特别是IE!)。
- 所以为了兼容旧版浏览器,在构建/打包项目时,希望将ES6的语法转为浏览器都可以支持的ES5。这时就需要使用babeljs帮助我们做这些事。
babel中文网: https://babel.bootcss.com/
推荐查看babel官方网站,应为中文网的翻译并不是最新的
webpack基础配置
- 在webpack中,babel通过loader的形式调用。
- 首先需要安装
babel-loader
及babel核心库@babel/core
npm install --save-dev babel-loader @babel/core
- 千万别忘了安装
@babel/preset-env
预设包
npm install --save-dev @babel/preset-env
- 之后需要在webpack的config文件中通过模块的方式引用。由于开发过程中有可能使用typescrpt,所以在打包时,需要将
.js
文件,react的.jsx
以及typescript的.ts
文件都使用babel编译为ES5语法。
module.exports = {
// ... other options
module: {
rules:[{
test: /.(js|ts)x?$/,
loader: 'babel-loader',
options:{
presets: ["@babel/preset-env"]
},
exclude: /node_modules/
},
{
//... other loader options
}]
}
}
- 配置好了之后执行
npx webpack
试试看:
// 源代码:
let array = [1,2,3,4,5];
array.map(item => item + 1)
// 打包后:
var array = [1, 2, 3, 4, 5];
array.map(function (item) {
return item + 1;
});
- 可以发现,在打包后
let
关键字被转为了var
关键字;Array.map
方法中的箭头函数(item) => item + 1
也被成功转为了ES5的function
但是!Array.map 是 ES6 数组扩展中新增的方法!尽管 babel 把 ES6 的语法转换为了 ES5 ,旧版本浏览器依然不支持 Array.map!
如果想要让低版本浏览器也能支持 ES6 的新增扩展,正常情况下就需要自己模拟实现 ES6 的扩展方法,然后通过prototype将方法插入JS对象的原型中,扩展原型方法。
那么,懒癌患者和前端新人怎么办?
不用怕!babeljs团队已经为我们准备好了一套比较完整的方法扩展(懒癌患者福音),我们只需要把扩展库引入就能让旧版浏览器支持 ES6 的新增扩展了 [/坏笑]
为代码添加 ES6 支持
- babeljs 提供了两种使用方法扩展的方式
@babel/polyfill
和@babel/plugin-transform-runtime
@babel/polyfill
- @babel/polyfill 官方给出的解释为,它可以效仿一个完整的 ES2015+ 运行环境,并意图运行于一个应用中而不是一个库/工具。
- 按照惯例,在使用
@babel/polyfill
前,需要先进行安装
npm install --save @babel/polyfill
- 安装之后只需要在业务代码头部引入就可以让旧版浏览器也支持 ES6,来测试一下
- 这里使用Promise测试,因为Promise是 ES6 新增的对象,并且IE所有版本浏览器在正常情况下均不支持
首先不添加 @babel/polyfill ,直接在控制台输出 Promise
// index.js
console.log(Promise) // 在控制台输出 Promise
使用 IE9 打开后,控制台抛出错误
然后我们引入 @babel/polyfill 后,再在控制台中输入 Promise
// 源代码:
require( '@babel/polyfill') // 使用commonJS语法引入polyfill
console.log(Promise) // 在控制台输出 Promise
依然使用 IE9 打开,可以发现控制台中成功打印出了 Promise 的构造函数
- 至此,IE也可以运行使用 ES6 编写的代码了! [/赞]
此处又有但是!可以运行并不意味着配置就完成了。
- 来看一下webpack的打包输出信息
what?! index.js文件里边只写了两行代码,打包后main.js的的大小竟然有207kb!
要是把业务代码都写进去,页面打开速度还不爆炸了!
- 这是因为,@babel/polyfill 在模拟 ES2015 环境时,会将添加的 ES6 方法一起打包到 main.js 文件当中。
那在当前代码中,我们只用到了Promise方法,能不能只把Promise的方法扩展打包?
-
肯定是可以的,查看一下相关文档,会发现在配置
@babel/preset-env
时,可以支持设置一个useBuiltIns
参数,支持3个值entry :
只支持引入一次@babel/polyfill
,如果多次引用会抛出错误usage :
只会将文件中用到的 ES6 方法引用到文件中false :
默认值,不会自动识别文件中使用的 ES6 方法,会将@babel/polyfill
作为整体进行填充
-
按照需求,最适合的应该是
usage
,它可以识别用到的 ES6 方法,只引入这些方法的扩展。这就很开心了,修改一下配置试试吧。
// webpack.config.js
module.exports = {
// ... other options
module: {
rules:[{
test: /.(js|ts)x?$/,
loader: 'babel-loader',
options:{
- presets: ["@babel/preset-env"],
+ presets: ["@babel/preset-env",{
+ "useBuiltIns": "usage"
+ }],
},
exclude: /node_modules/
},
{
//... other loader options
}]
}
}
修改完成之后,再执行一次打包
可以看到,打包后的文件大小降至29.5kb。 (这里由于使用了其他插件,所以文件依然比较大,在未使用其他插件情况下,打包出来的文件大约3kb)
- 到这里,
@babel/polyfill
的配置才算是比较符合需求了
之所以说以上针对babel的配置是比较符合要求,是因为 @babel/polyfill 虽然可以帮助开发者注入使用到的 ES6 方法,但是它是以全局变量的形式将方法注入。如果只是业务开发使用,问题并不是很大。但是如果在开发类库或UI组件时,全局注入的方式会造成变量的全局污染。这不是我们期望的结果。我们更希望在注入方法的同时保持全局环境的清洁。这就需要用到 @babel/plugin-transform-runtime 插件
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
会以闭包的形式注入 ES6 方法,可以最大限度的保证全局环境不被污染- 首先,依然需要安装插件
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
- 安装完成后,需要在配置文件中调用插件,替换
"@babel/preset-env"
的相关配置
// webpack.config.js
module.exports = {
// ... other options
module: {
rules:[{
test: /.(js|ts)x?$/,
loader: 'babel-loader',
options:{
- presets: ["@babel/preset-env",{
- "useBuiltIns": "usage"
- }],
+ plugins: [
+ ["@babel/plugin-transform-runtime", {
+ "corejs": 2,
+ "helpers": true,
+ "regenerator": true,
+ "useESModules": false
+ }]
+ ]
},
exclude: /node_modules/
},
{
//... other loader options
}]
}
}
- 按照官方文档的说明,如果给
corejs
参数配置number
类型的值,则需要使用@babel/runtime-corejs2
代替@babel/runtime
注入,如果值为false
则不需要做任何修改 - 由于这里使用了
number
类型的值,所以需要卸载@babel/runtime
npm uninstall --save @babel/runtime
- 然后安装
@babel/runtime-corejs2
npm install --save @babel/runtime-corejs2
- 替换完成后,运行打包命令即可
.babelrc
- 在webpack中,如果babel的配置比较多, options 的配置就会变的相当长一旦出现修改,找起来会相当酸爽。
可以将options中的所有配置都移动到.babelrc文件中单独书写
- 首先在根目录下创建一个.babelrc文件
touch .babelrc
- 然后将babel的相关文件从配置文件中拿出来,丢进.babelrc文件中
// webpack.config.js
module.exports = {
// ... other options
module: {
rules:[{
test: /.(js|ts)x?$/,
loader: 'babel-loader',
- options:{
- plugins: [
- ["@babel/plugin-transform-runtime", {
- "corejs": 2,
- "helpers": true,
- "regenerator": true,
- "useESModules": false
- }]
- ]
- },
exclude: /node_modules/
},
{
//... other loader options
}]
}
}
{
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]
]
}
目前通过babel转换后的代码只能支持 IE9+ 无法支持 IE8 及以下浏览器。原因是打包后的文件当中包含了Object.defineProperty方法,IE8 及以下浏览器不支持此方法。暂时还未找到比较完美的解决方案。希望有相关经验经验的小伙伴能帮助解答。
参考资料