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家族其他的一些插件需要配置,后續會繼續更新。

