webpackの続きです。これまでフロント開発3銃士のうちGulpとBabelを触ってみたので、今回は最後の一つ、webpackを触ってみる。以前のJSモジュールの記事のjsファイルをバンドルしてみました。それでは行きましょう!
【目次】
- JSモジュールのおさらい
- importモジュールのjsファイルを読み込むとエラーになる
- モジュール読み込みはローカルだと動かない(異ドメイン間と認識)
- webpackでモジュールをバンドルするんですよ!キミィ
- webpackをインストール
- モジュールファイルを作る
- webpackを実行!
- package.jsonを書き換えて
- webpack.config.jsファイルで詳細設定
- 圧縮せずにjsファイルをバンドル
- index.htmlを作ってscript.jsをリンク
- おまけ:モジュールの記述ミスで起きたエラー
- 最後に
前回記事
※参考:webpackを理解するために調べたこと(Webデザイナー→フロントエンドエンジニアへの脱皮) - クモのようにコツコツと
Web開発環境まとめ
qiita.com
JSモジュールのおさらい
以前のモジュールの記事ではHTMLファイルの中にimport
のJSコードを書いていた。
exsport
を書いたJSファイル(export.js)を…
export default function (massage) { alert(massage); }
HTMLファイルで読み込むと…
<script type="module"> import aisatsu from "./js/export.js"; aisatsu("また会えたね、モジュール君♪"); </script>
アラートが表示された。
詳細はこちらの記事を参照
※参考:【JS】モジュール(import / export)でどんなことができるのか事始めてみた - クモのようにコツコツと
importモジュールのjsファイルを読み込むとエラーになる
この形を少し変えて、import.js
にimportモジュールを書く。
import aisatsu from "./export.js"; aisatsu("親子で再開、モジュール君♪");
このimport.js
をHTMLファイルで読み込むと…
<script type="module" src="js/import.js"></script>
シーン…返事がない。ただの屍のようだ。
デベロッパーツールを確認すると下記のエラーになっている。
Access to script at '(ファイルのパス)/js/import.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https. Failed to load resource: net::ERR_FAILED
Google先生で翻訳すると
オリジン「null」から「(ファイルのパス)/js/import.js」のスクリプトへのアクセスはCORSポリシーによってブロックされています。クロスオリジンリクエストは、プロトコルスキームでのみサポートされています:http、data、chrome、chrome-extension、https 。
リソースの読み込みに失敗しました:net :: ERR_FAILED
うむ、よくわかんない。。
モジュール読み込みはローカルだと動かない(異ドメイン間と認識)
エラーコードをもとに調べると、こちらのQAと同じ現象のようだった。
※参考:JavaScript - JavaScript 別ファイルからクラスをインポートしたい|teratail
原因がわかりました(中略)CORSエラーです。
ES Moduleは、どうやら、JavaScript上での直接ファイルアクセスとなるようですね。
つまりは、ローカルでは、同じディレクトリ上でも、そもそもドメインがないので、
異ドメイン間と認識されるのかと思われます。
そうなると、異ドメイン間のスクリプトからのファイル直でのアクセスは、ブラウザによってセキュリティが働くため、基本行えません。
サーバ上かMAMPなどのローカルサーバなら動きそうとのことで、MAMPにフォルダを投げてみると…
アラートが表示される!
caniuseによればモジュールはIE以外のモダンブラウザは全て対応済みなのだが、ローカルでの挙動は注意が必要っぽい。
※参考:Can I use... Support tables for HTML5, CSS3, etc
ブラウザのセキュリティ対策による結果のため、これはまぁ仕方がないかなぁと思う。
webpackでモジュールをバンドルするんですよ!キミィ
そこでwebpackの登場と相成るわけですな!
webpackとは何ぞや?以前も調べた。
※参考:webpackを理解するために調べたこと(Webデザイナー→フロントエンドエンジニアへの脱皮) - クモのようにコツコツと
が、この時点では「webpack一つあればフロントエンド開発のいろんなことができるぞわ〜い」というテンションだった。
改めてwebpackの中心となる複数ファイルのバンドル機能に絞って調べてみる。
こちらの解説がとてもわかりやすかった!
※参考:最新版で学ぶwebpack 4入門 - JavaScriptのモジュールバンドラ - ICS MEDIA
webpackとはウェブコンテンツを構成するファイルをまとめてしまうツールです。一番多い使い方は、複数のJavaScriptを1つにまとめることでしょう。複数のJavaScriptをまとめるのは、いろんな利点があります。
そのメリットとは…
- 転送の最適化
- モジュールが使える
- JSだけでなく、CSSや画像もバンドルできる
- 包括的な開発環境が整う
こちらの概念図もわかりやすい!
CSSや画像のバンドルについては個人的にはまだあまり必要性を感じていないのだが、手始めとしてJSのモジュールファイルをバンドルしたい!
なお、webpackはGulp、Babelとともに「フロント開発3銃士」と呼ばれるツール。
※参考:gulpとbabelとwebpackというフロント開発3銃士 - Qiita
webpack以外はすでに触れているので本記事をもって3つ全てを体験したことになる!それぞれの位置付けの理解もより深まるはず!
webpackをインストール
先ほどのページを参考に進めてみる。
※参考:最新版で学ぶwebpack 4入門 - JavaScriptのモジュールバンドラ - ICS MEDIA
node.jsとnpmは入っている前提(下記のコマンドでバージョン番号が出る)
node -v npm -v
webpackを入れるフォルダを作る。今回はwebpack_test
とした。
cdコマンドでフォルダに移動。
cd (フォルダのパス)/webpack_test
package.jsonを作成する。
npm init -y
ここまではここ最近のフロントエンド環境の記事と共通する内容。
webpack
とwebpack-cli
をインストールする。
npm i -D webpack webpack-cli
二つ繋げて書けば同時にインストールされる。
成功するとpackage.json
に追記される
"devDependencies": { "webpack": "^4.41.5", "webpack-cli": "^3.3.10" }
モジュールファイルを作る
src
というディレクトリを作り、その中にindex.js
とsub.js
を作成。
. ├node_modules(中身は省略) ├src │├index.js │└sub.js ├package-lock.json └package.json
sub.jsにはexportするモジュールを書く。
export default function (massage) { alert(massage); }
index.jsにはinportするモジュールを書く。
import aisatsu from "./sub.js"; aisatsu("こんにちは、うぇぶぱっく。");
先ほどのモジュールをベースにしてファイル名を変更。
webpackを実行!
それではいよいよ、webpackを実行!下記のコマンド
npx webpack
お、dist
というフォルダがでけた!
. ├dist │└main.js ├node_modules(中身は省略) ├src │├index.js │└sub.js ├package-lock.json └package.json
中にはmain.js
という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);alert("こんにちは、うぇぶぱっく。")}]);
改行なしの長〜いコードが書き出されている!!
よくライブラリであるminify(最小化)ファイルみたいね。jquery.min.js
とかのアレね。
package.jsonを書き換えて
package.jsonのscript
キーにwebpackのビルドコマンドを追加する。
{ "scripts": { "build": "webpack", },
build
キーの値をwebpack
に。
これでnpmコマンドで実行ができるようになる。
npm run build
書き出されるコードは同じ。
npmコマンドについてはこちらも参照
※参考:Node.jsユーザーなら押さえておきたいnpm-scriptsのタスク実行方法まとめ - ICS MEDIA
webpack.config.jsファイルで詳細設定
webpackの詳細設定をするためにwebpack.config.jsファイルを作る。
. ├dist │└main.js ├node_modules(中身は省略) ├src │├index.js │└sub.js ├package-lock.json ├package.json └webpack.config.js
webpack.config.jsにエントリーポイントと出力設定を書く。
module.exports = { // エントリーポイント entry: `./src/index.js`, // 出力設定 output: { // ディレクトリ path: `${__dirname}/dist`, // ファイル名 filename: "main.js" } };
エントリーポイントとはバンドルするときにメインにするファイル。依存関係があった時に優先される。
先ほどのフォルダ名、ファイル名はwebpackであらかじめ設定されている名前だが、webpack.config.jsを作ることでこのフォルダ名、ファイル名を独自なものに変えることができる。
module.exports = { // エントリーポイント entry: `./src/index.js`, // 出力設定 output: { // ディレクトリ path: `${__dirname}/test/js`, // ファイル名 filename: "script.js" }, //圧縮(production)モードを解除 mode: "development" };
- 出力先を
/test/js/script.js
に変更 - 出力モード
mode
をdevelopment
に変更(圧縮しない)
ちなみに圧縮モードはproduction
圧縮せずにjsファイルをバンドル
コマンドを再度実行!
npm run build
フォルダとファイルが書き出された!
. ├dist │└main.js ├node_modules(中身は省略) ├src │├index.js │└sub.js ├test │├js │ └script.js ├package-lock.json ├package.json └webpack.config.js
script.jsを開いてみると…
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /*(中略)*/ "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sub_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sub.js */ \"./src/sub.js\");\n\nObject(_sub_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"こんにちは、うぇぶぱっく。\");\n\n//# sourceURL=webpack:///./src/index.js?"); /***/ }), /*(後略)*/
うわぁすごい。。想像しているより結構な長さのコードになった。「ソースマップ」と言って元のファイルからのバンドルの過程の情報も含まれているからこんなに膨大になるらしい。
詳しくは読み解けないのだが、だいぶ下の方にあるeval()
の引数に「こんにちは、うぇぶぱっく。」の文字が見受けられる。
eval() は文字列として表された JavaScript コードを式として評価する関数です。
そうなんだ。
index.htmlを作ってscript.jsをリンク
testフォルダの直下にindex.html
を作る。
. ├dist │└main.js ├node_modules(中身は省略) ├src │├index.js │└sub.js ├test │├js ││ └script.js │└inde.html ├package-lock.json ├package.json └webpack.config.js
index.html
でscript.js
をリンクする。
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>はじめてのwebpack</title> </head> <body> <script src="js/script.js"></script> </body> </html>
index.html
をブラウザで開いてみると…
やた!アラートが表示された!!
ちなみに先ほど変更したモードを圧縮モードに戻しても結果は同じだった。
module.exports = { //(中略) //圧縮モードに戻す mode: "production" };
おまけ:モジュールの記述ミスで起きたエラー
実は最初にインポート側(index.js
)で関数名を付ける書式(ブロック{ }
無し)を間違えてブロック有りで書いていてしまっていた。
import { aisatsu } from "./export.js"; aisatsu("親子で再開、モジュール君♪");
そしたらwebpackのバンドル時にターミナルで「aisaatsu」という関数名が見つからない、という警告が出た。
WARNING in ./src/index.js 2:0-7 "export 'aisatsu' was not found in './sub.js'
それに気が付かずにブラウザで開いたらデベロッパーツール でエラーになった。
Uncaught TypeError: (void 0) is not a function
index.js
のブロックを削除したら、エラーが出なくなった。
import aisatsu from "./export.js"; aisatsu("親子で再開、モジュール君♪");
もしかしたら似た現象が起きる方がいるかもしれないので備忘録までに記録。
最後に
ということでwebpackでjsファイルのバンドルを初めて行いました!これでGulp、Babel、webpackのフロント開発3銃士が勢揃いしました(この呼び名、気に入ってますw)
Vue.jsやReactなどでかなりの数のモジュールファイルが分かれそうに思うので、この機能を連携することになりそうです。しかし、CSSや画像ファイルは実際のフロントエンド開発現場ではJSファイルにバンドルしているのでしょうかね?
他のツールとの連携なども含め、引き続き調べて行きたく思います。それではまた!
Web開発環境まとめ
qiita.com