webpack實踐(五)- babel的基礎配置和使用
webpack系列部落格中程式碼均在github上:https://github.com/JEmbrace/webpack-practice
《webpack實踐(一)- 先入個門》
《webpack實踐(二)- webpack配置檔案》
《webpack實踐(三)- html-webpack-plugin》
《webpack實踐(四)- html-webpack-plugin》
《webpack實踐(五)- babel的基礎配置和使用》
一.前言
前面我們總結了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屬性設定。
然後我們在執行打包命令
咦,又報錯了。
這個問題呢,在本篇文章中,還是因為版本問題: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 [email protected] @babel/core @babel/preset-env
低版本安裝命令為npm install [email protected] babel-core babel-preset-env
同時不同版本在配置預設的時候傳入的包名稱也是不一樣的,文章中也有提到。
下一節,會在總結babel編譯過程中的一些內容,同時在總結一下babel家族中其他的一些外掛包。