webpack实践(五)- babel的基础配置和使用
webpack系列博客中代码均在github上:https://github.com/JEmbrace/webpack-practice
《webpack实践(三)- html-webpack-plugin》
《webpack实践(四)- html-webpack-plugin》
一.前言
前面我们总结了html-webpack-plugin这webpack插件的可选配置项:template、title、filename、inject和minify。html-webpack-plugin它可以帮我们生成一个默认模板或者配置使用我们自己的模板,将打包后的js文件自动引入模板中;它还可以压缩模板中的css、javascript代码。
这篇文章就要来学习一个新的东西:babel。我们在做项目开发的时候,经常会想使用ES5+的新语法,然而各个浏览器对ES5+语法支持程度也都不一致,为了我们编写的代码可以运行在不同的浏览器上,最好是能将这些语法编译成为ES5的语法,那么这个bable就是帮我们完成这个编译工作的。
二.安装
结合官方文档,要想babel帮我们完成这个编译工作,需要分别安装:babel-loader、babel-core、babel-preset-env这个三个插件包。
备注:使用我的命令安装的bable-loader版本为8.0.6,而安装的babel-core和babel-preset-env版本均于babel-loader不匹配,在后续的打包过程也有错误出现,后面会告诉大家怎么去解决。若大家想不想反复有错误出现,可以跳转至文章最后查看正确的安装命令。
三.使用
1.webpack.config.js配置
首先我们需要在webpack.config.js中配置如下选项
然后需要在rules中添加一条规则,这条规则需要明确指定两个内容:
那些js文件需要编译处理
需要编译处理的文件使用哪个插件处理
那么下面我们就贴出一个最简单的配置代码
webpack.config.js
var htmlWepackPlugin = require('html-webpack-plugin') var path = require('path'); module.exports = { mode: 'development', entry: { main: './index.js' }, output: { path: path.resolve(__dirname,'dist'), filename: 'index.bundle.js' }, plugins:[ new htmlWepackPlugin({ title: 'webpack实践(五)- babel-loader', template: './index.html', filename: 'template/resultIndex.html', inject: 'head', minify: { removeComments: true, minifyCSS: true, minifyJS: true } }) ], module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader' } } ] } };
2.新建包含ES5+的代码文件
接着,我们来使用ES6中的一些语法编写一些代码。
在项目根目录下新建文件counter.js
备注:dist目录、index.html、index.js均是前几节中的文件。dist目录可以删除,其余两个文件后续还会使用到。
编写counter.js文件
/* * 将两个数做对应的计算并返回结果 * 使用到的ES6的基本语法有: * 函数扩展-为函数参数设置默认值 * 模块导出-export命令 */ class Counter { constructor(x=0,y=0){ this.x = x; this.y = y; } add(){ return this.x+this.y; } } export { Counter };
然后在编辑项目根目录下的index.js
/* * 引用counter.js模块中的函数,传入参数,打印相加结果 * 使用到的ES6的基本语法有: * 模块导入-import命令 * 变量声明命令-let */ import { Counter } from './counter.js' let a = 100; let b = 200; let counter = new Counter(a,b); let result = counter.add(); console.log("add: a + b = " + result);
编辑项目根目录下的index.html
<html> <head> <meta charset="utf-8" /> <title><%= htmlWebpackPlugin.options.title %></title> <style> /* 写点样式 */ h1{ font-size: 12px; color: #ccc; } </style> </head> <body> <!-- 这里是h1标签 --> <h1>webpack实践(五)- bable-loader</h1> </body> </html>
3.打包查看结果
在项目根目录下运行webpack打包命令
可以看到报错了:can't find module @babel/core,在本篇文章中,出错的原因是我们的babel-core版本不对,查看我们的package.json文件
我们安装的bable-loader版本为8.0.6,而babel-core的版本为6.26.3,那官方文档中指示我们bable-loader8需要结合bable-core7,或者bable-loader7结合babel-core6。
因此我们可以降低bable-loader的版本为7.x,或者升级bable-core的版本为7.x,本次我们选择将babel-core升级为7.x去解决这个问题。
先卸载bable-core
重新安装bable-core
备注:安装babel-core7.x使用npm install @babel/core
可以看到我们已经成功安装了babel-core7.7.7
现在我们在重新进行打包:
打包完成,我们使用浏览器打开打包后的结果文件dist/template/resultIndex.html
可以看到界面已经展示出效果,控制台也打印出正确结果。
然后我们在查看一下我们的dist/index.bundle.js(这里只贴出部分重要代码;并且为了方便观看,将eval中的字符串进行手动了换行)
***/ "./counter.js": /*!********************!*\ !*** ./counter.js ***! \********************/ /*! exports provided: Counter */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Counter\", function() { return Counter; }); \n/*\r\n*\t将两个数做对应的计算并返回结果 \r\n*\t使用到的ES6的基本语法有: \r\n*\t\t函数扩展-为函数参数设置默认值 \r\n*\t\t模块导出-export命令 \r\n*/\n class Counter {\n constructor(x = 0, y = 0) {\n this.x = x;\n this.y = y;\n }\n\n add() {\n return this.x + this.y;\n }\n\n}\n\n\n\n//# sourceURL=webpack:///./counter.js?"); /***/ }), /***/ "./index.js": /*!******************!*\ !*** ./index.js ***! \******************/ /*! no exports provided */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _counter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./counter.js */ \"./counter.js\"); \n/*\r\n*\t引用counter.js模块中的函数,传入参数,打印相加结果 \r\n*\t使用到的ES6的基本语法有: \r\n* 模块导入-import命令\r\n*\t\t变量声明命令-let \r\n*/\n\n let a = 100;\n let b = 200;\n let counter = new _counter_js__WEBPACK_IMPORTED_MODULE_0__[\"Counter\"](a, b);\n let result = counter.add();\n console.log(\"add: a + b = \" + result);\n\n//# sourceURL=webpack:///./index.js?");
我们可以看到,关于index.js和counter.js中使用ES6语法编写的代码貌似原封不动的存于于打包的结果文件中。关于这个问题呢,是因为我们的webpack.config.js文件缺失一项配置:Presets,即我们最开始安装的babel-preset-evn。
这里刚开始会有一个疑问,就是为什么结果文件并没有将ES6的语法编译,而浏览器却能打印出正常的结果。
后面再经过一系列探究和实践,发现chrome已经可以运行ES6中的let语法和函数扩展-为函数参数设置默认值语法。
而关于模块导出/导出的import和export语法,chrome本身并不支持,而是webpack将其进行了转化,这个转化结果浏览器可解析。
因此最终我们在浏览器可以正常打印出结果。
babel-preset-env它是一个预设的插件,它会根据我们代码运行的目标环境去编译那些目标环境不支持的特性。因此我们还需要配置这个选项,配置方法就是通过options属性设置。
(关于预设的内容,在作者的另一篇文章中有简单介绍 《ES6-babel环境搭建》)
然后我们在运行打包命令
咦,又报错了。
这个问题呢,在本篇文章中,还是因为版本问题:babel-preset-env版本和babel-loader版本不匹配。看下面package.json的配置,可以看到babel-preset-env的版本是1.7.0
那我们依然是卸载之前安装的babel-preset-env安装高版本的。
重新安装
备注:安装高版本的babel-preset-env依然需要使用npm instal @babel/preset-env
此时我们查看package.json文件,可以看到@babel/preset-env已经成功升级安装到7.7.7
因为版本升级后,插件包的名称改变,因此我们还需要修改options.presets中设置的包名称
然后在进行打包
这次没有报错,为了确认ES6的代码被成功编译,我们在看一下dist/index.bundle.js文件中的内容(这里依然还是贴出部分重要代码;并且为了方便观看,将eval中的字符串进行手动了换行)
/***/ "./counter.js": /*!********************!*\ !*** ./counter.js ***! \********************/ /*! exports provided: Counter */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Counter\", function() { return Counter; });\n function _classCallCheck(instance, Constructor) { i f (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\n function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n /*\r\n*\t将两个数做对应的计算并返回结果\r\n*\t 使用到的ES6的基本语法有:\r\n*\t\t函数扩展-为函数参数设置默认值\r\n*\t\t模块导出-export命令\r\n*/\n var Counter =\n/*#__PURE__*/\n function () {\n function Counter() {\n var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n _classCallCheck(this, Counter);\n\n this.x = x;\n this.y = y;\n }\n\n _createClass(Counter, [{\n key: \"add\",\n value: function add() {\n return this.x + this.y;\n }\n }]);\n\n return Counter;\n }();\n\n\n\n//# sourceURL=webpack:///./counter.js?"); /***/ }), /***/ "./index.js": /*!******************!*\ !*** ./index.js ***! \******************/ /*! no exports provided */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _counter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./counter.js */ \"./counter.js\"); \n/*\r\n*\t引用counter.js模块中的函数,传入参数,打印相加结果\r\n*\t使用到的ES6的基本语法有:\r\n* 模块导入-import命令\r\n*\t\t变量声明命令-let\r\n*/\n\n var a = 100;\n var b = 200;\n var counter = new _counter_js__WEBPACK_IMPORTED_MODULE_0__[\"Counter\"](a, b);\n var result = counter.add();\n console.log(\"add: a + b = \" + result);\n\n//# sourceURL=webpack:///./index.js?"); /***/ })
这次明显能看出来,index.js和counte.js中的一些ES6语法均被编译成为ES5的语法:let编译为var;函数的默认参数转化为arguments的逻辑。
四.总结
然后这一节简单使用babel编译ES6代码就结束了。
在此总结一下:
webpack中使用babel编译包含ES6语法的js文件,需要依次安装babel-loader、babel-core、babel-preset-env。
备注:这里一定一定要关注这几个插件包的版本。前面我演示的过程中,安装命令为npm install babel-loader babel-core babel-preset-env,但实际上安装后的版本分别为 8.x、6.x和1.x。
如果大家不想在出错后反复卸载重装:
高版本安装命令为npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env
低版本安装命令为npm install babel-loader@7.1.4 babel-core babel-preset-env
同时不同版本在配置预设的时候传入的包名称也是不一样的,文章中也有提到。
在实际项目开发中还会用到babel家族其他的一些插件需要配置,后续会继续更新。