今時のフロントエンドについて学ぼうとすると、"webpack" という単語が出てくることがあるのですが、よくわからないので先になかなか進めないということがあります。
ということで、今回はwebpackに触れてみたいと思います。
webpackとは
webpackとはCSSやJavaScript、画像などWebサイトを構成するあらゆるファイルを1つにまとめるモジュールバンドラーのことです。
webpackを利用して複数ファイルをバンドルすることは以下のような利点があります。
- 依存関係の解決
- リクエスト数を減らす
- ソースコードの可読性の向上
webpack のインストール
以下のコマンドでグローバルインストールできます。
$ npm install -g webpack
ただ、グローバルインストールは他のディレクトリにも影響を及ぼすので、専ら以下のように "--save-dev" オプションをつけて ローカルインストールをすることが多そうです。
$ npm init -y /* 初期設定で設定ファイルを生成 */
/* webpack-cli コマンドラインで実行可能なwebpack。 webpackコマンドなどが使えるようになる */
$ npm install --save-dev webpack webpack-cli
これだけで、webpackのインストールができます。次にwebpackの設定をしていきますが、その前に今回想定する構成を下に示しておきます。
.
├── package.json /* npm の設定ファイル */
├── node_module /* npm のモジュール群 */
├── app /* 公開ディレクトリ */
│ ├── index.html
│ └── js
│ └── bundle.js /* webpackでまとめたものを出力するファイル */
├── src /* モージュールやライブラリのディレクトリ */
│ └── js
│ ├── entry.js /* エントリーポイント */
│ └── modules /* モジュール群 */
│ ├── sample-module.js
│ └── calculator-module.js
└── webpack.config.js /* webpack の設定ファイル */
webpackの設定
webpackを利用するために、webpack.config.js に設定を記述していきます。
最低限、mode, entry, output の3つの設定を記述する必要があります。
[webpack.config.js]
/* output.pathに絶対パスを指定するため、pathモジュールが必要 */
const path = require('path');
module.exports = {
/* モードの設定 webpack4からは必須の設定*/
mode: 'development',
/* エントリーポイントの設定 */
entry: './src/js/entry.js',
/* 出力の設定 */
output: {
/* 出力するファイル名 */
filename: 'bundle.js',
/* 出力先のパス(絶対パス) */
path: path.join(__dirname, 'app/js')
}
};
各設定について
mode
モード(webpack 4から追加された項目)の設定。
"development"、"production"、"none" のいずれかを記述。
"development"か"production"を指定すると、様々な設定が有効になってwebpackが実行される。("production"ではソースマップが有効になったり、"production"にすれば圧縮されたファイルが出力されたりする。)
entry
エントリーポイントの設定。複数設定することも可能。
エントリーポイントとは、各モジュールを読み込んでメインの処理をする、モジュール間の依存関係の解析を開始する地点のこと。
output
出力の設定。
出力するファイル名や出力先のパスを指定する。
OSによってパスが異なることを防ぐために、出力先のパスの指定にはpath.join()を利用する。
webpackを使ってモジュールをまとめてみる
使用するモジュールについて
実際に、モジュールをまとめて使用してみたいと思います。
今回、src/js/module/配下の、"sample-module.js"、"calculator-module.js" には以下のように書いてみました。
[sample-module.js]
export default function sampleOutput() {
console.log('sample-test');
}
[calculator-module.js]
export default function multiplication(number1 ,number2) {
return number1 * number2;
}
エントリーポイント(entry.js)
entry.js には以下のように記述します。
import sampleOutput from './modules/sample-module';
import multiplication from './modules/calculator-module';
sampleOutput();
var item = 10;
var price = 120;
console.log(multiplication(item, price));
※実用上はこのファイルに関数の実行などの処理は記述せず、モジュールの依存関係を管理するために使用するようですが、今回は簡単にするためにこちらで実行しています。
バンドルする
webpackコマンドを使用します。
実行すると、必要となるモジュールを読み込んで bundle.js に出力してくれます。
$ node_modules/.bin/webpack /* グローバルインストールなら webpack のみでOK */
Hash: e804108f0ce8cc75a79b
Version: webpack 4.42.0
Time: 189ms
Built at: 03/04/2020 9:15:47 AM
Asset Size Chunks Chunk Names
bundle.js 5.68 KiB main [emitted] main
Entrypoint main = bundle.js
[./src/js/entry.js] 202 bytes {main} [built]
[./src/js/modules/calculator-module.js] 89 bytes {main} [built]
[./src/js/modules/sample-module.js] 73 bytes {main} [built]
出力された bundle.js は以下の通りです。
~中略~
/***/ "./src/js/entry.js":
/*!*************************!*\
!*** ./src/js/entry.js ***!
\*************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_sample_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./modules/sample-module */ \"./src/js/modules/sample-module.js\");\n/* harmony import */ var _modules_calculator_module__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./modules/calculator-module */ \"./src/js/modules/calculator-module.js\");\n\n\n\nObject(_modules_sample_module__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n\nvar item = 10;\nvar price = 120;\nconsole.log(Object(_modules_calculator_module__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(item, price));\n\n\n//# sourceURL=webpack:///./src/js/entry.js?");
/***/ }),
/***/ "./src/js/modules/calculator-module.js":
/*!*********************************************!*\
!*** ./src/js/modules/calculator-module.js ***!
\*********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return multiplication; });\nfunction multiplication(number1 ,number2) {\n return number1 * number2;\n}\n\n\n//# sourceURL=webpack:///./src/js/modules/calculator-module.js?");
/***/ }),
/***/ "./src/js/modules/sample-module.js":
/*!*****************************************!*\
!*** ./src/js/modules/sample-module.js ***!
\*****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return sampleOutput; });\nfunction sampleOutput() {\n console.log('sample-test');\n}\n\n\n//# sourceURL=webpack:///./src/js/modules/sample-module.js?");
/***/ })
/******/ });
読み込んだモジュール(sampleOutput() や multiplication())を探してみるとeval() の中にあります。eval()を使ってモジュールをバンドルしているようです。
webpack.config.js の mode を "production" に設定すると、圧縮されて bundle.js が出力されます。
[bundle.js]
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t),console.log("sample-test");console.log(10*120)}]);
実際に使用する際には、app/index.html で bundle.js を読み込みます。
実際にブラウザからアクセスしてみると、正しく読み込みているのがわかります。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>webpack test</title>
</head>
<body>
<script src='js/bundle.js'></script>
</body>
</html>