webpackでブラウザJSの開発環境を構築
最近、Web用のJavaScriptライブラリをバンドルする事が増えてきたので、webpackでのJavaScript開発環境の基本をまとめておきます。
ライブラリとかパッケージという言葉を、筆者はあまり区別せずに用いていますが、ご容赦ください。ライブラリは機能のまとまり、パッケージはnpmで配布しているプログラムのまとまり、ぐらいの意味で使っています。
環境は、Windows11、webpackでのバンドルを想定しています。
また開発のサンプルとして、HTMLのDOMをイメージ画像化するnpmのライブラリhtml-to-imageを組み込んでみます。
環境のインストールと設定
まずシステム領域ではない場所に、プロジェクト用のフォルダを作成します。筆者は古い人間で、パスにマルチバイト文字や空白等が含まれていると失敗する経験則があるので、たいていはdドライブの浅い階層に英数のフォルダを作成します。ここではd:¥testpとしました。
PowerShellで、該当のフォルダに移動してnode環境をイニシャルします。node.jsのインストールに関しては、別途記事がありますのでよかったら目をとおしてみてください。
npm init ...
npm init実行後いろいろ対話式に聞かれますが、ここでの回答はあまり重要ではないのですべてデフォルト値のまま進めてしまって問題はありません。ここでの回答はプロジェクトルートフォルダのpackage.jsonファイルに保存されます。
このpackage.jsonファイルはプロジェクトのメタ情報(どのようなライブラリに依存しているかなどの情報)を保存しておくものです。初期化時点ではおおむね次のようになっているのではないかと思います。
同時に生成されるpackage-lock.jsonも同様にメタ情報を保存するためのファイルで、こちらは複数人数での開発の際に各パッケージのバージョンを揃えたりする際に用いられます。そのようなケースがなければあまり意識する必要はありません。
package.json
{
"name": "testp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
}
開発環境であるwebpackをインストールします。webpackはJavaScriptのモジュールをバンドルして(束ねて)ひとつのファイルにしてくれるもので、その過程で空白をつめてコードを圧縮してくれたり、使っていないメソッドを取り除いてくれたりもします。
webpack-cliはバンドル処理時のコマンドラインのインターフェース、webpack-dev-serverはコードテスト用のサーバーとなっていて、こちらも合わせてインストールします。
npm install --save-dev webpack webpack-cli webpack-dev-server ...
--save-devオプションは開発環境にインストールするという意味です。コードをwebpackでバンドルはしますが、このあと作成するJavaScriptのコードではwebpack本体は利用しないため、それを明示してインストールしています。
インストール作業をすると、package.jsonファイルに次のようにdevDependencies(開発時依存パッケージ一覧)の記述が加わります。
package.json
{
...
"devDependencies": {
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
}
}
また「node_modules」というフォルダも生成されると思います。ここにインストールしたライブラリが入ります。またNode.jsの基本的なライブラリも含まれます。
プロジェクトフォルダの下にサブフォルダを作ります。これは必須ではありません。また、人により構成や命名も変わってくる部分です。
筆者は開発用のソースコードをおく「src」、バンドル済みの完成品をおく「dist」、テスト用の「public」フォルダを作成しました。
webpack設定ファイル。webpackで複数のコードをバンドルする際に利用する設定ファイルを作成します。こちらの名前は固定されており「webpack.config.js」となっています。これをプロジェクトのルートフォルダに作成します。
拡張子が.jsとなっていることからもわかるように、これはJavaScriptのコードとなっています。モジュール式で記述します。
webpack.config.js
// Node.jsの標準ライブラリです。
// これを使ってフルパスを取得します。
const path = require('path');
const strExpDir = path.resolve(__dirname, 'dist');
module.exports = {
// コードのエントリーポイントとなるjsファイル
entry: './src/index.js',
output: {
// 完成品のファイル名
filename: 'main.js',
// 完成品出力フォルダ
path: strExpDir
},
// 出力モード production or development
mode: 'development',
}
あとからでもいいですが、テストサーバーの稼働フォルダにindex.htmlを作成しておきます。
これで一通りの用意ができました。
開発の進め方
エントリーポイントのJavaScriptファイルを作成し、このスクリプトを起点にコードを書きます。さきほど、webpack.config.jsファイルのentryに./src/index.jsを指定したので、ここではsrcフォルダの中にindex.jsファイルを作成します。
index.js
console.log("hello world");
document.body.appendChild(document.createTextNode("test"));
外部のライブラリを何もインポートをしていませんが、この状態でもwebpackを使ってバンドルすることができます。
webpackをNode.js環境から呼び出す方法には主にふたつの方法があります。ひとつは「npx webpack」とする方法、もうひとつはpackage.jsonにエントリーを書いた上で、「npm run」とする方法です。
npxはデフォルトで利用可能なコマンドですが、古くはパッケージのひとつだったようです。指定したパッケージのコマンドを直接実行することができます。未インストールのパッケージを指定した場合も自動で設定し実行してくれます。(未インストールだった場合は実行後は自動削除されます)。
npx webpack
先ほどのwebpack.config.jsファイルに設定を書きましたがここで引数としてオプションを指定する事もできます。
npm run でスクリプトを書く場合は次のようにpackage.jsonファイルに記述します。コマンド名やパラメータなどは環境にあわせておこなってください。
package.json
{
"name": "testp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
}
ここでは「build」という名前で割り当てたので、次のようにしてバンドルを実行します。
どちらかの方法でwebpackを実行すると、出力先に指定したフォルダにファイルが生成されます。これをコピーして、デバッグサーバーフォルダに配置し、htmlファイルからロードするように記述します。
index.html
...
<script src="./main.js"></script>
テスト
テストはここではさきほどインストールしたwebpack-dev-serverを用いますが、自身でapacheサーバーを立ててそこで行うことにしてもまったく問題はありません。
次のようにコマンドを実行すると、テストサーバーが起動します。
npx webpack serve
このコマンドは先ほど同様に、スクリプト化してnpm runで実行することもできますし、npx webpack-dev-serverとしても動きます。
ここでindex.htmlを配置した場所が、「public」フォルダでない場合は、「--static "フォルダ名"」として、プロジェクト下にある任意のWebデバッグ用のフォルダを指定できます。
通常、ローカルホストの8081番ポートでテストサーバが稼働しますので、ブラウザでアクセスすることで自身の作ったJavaScriptコードのテストができます。もしくは、--openオプションを加えると、ブラウザを自動でオープンしてくれます。
html-to-image
今度はライブラリをインポートするサンプルを書きたいと思います。利用するのはDOMを画像化するhtml-to-imageというライブラリです。
ただ、コードを書く前に、前の環境に修正を加えます。
ここまでわかりやすいようにテストサーバーフォルダと、完成品の出力先を分けましたがそれを同じフォルダにします。
webpackの機能のひとつとして、ソースコードに修正が加わった場合自動で再バンドルしてくれる機能があるためです。
それを利用して、ソースコードの修正を即座にブラウザに反映されるようにします。
完成品出力先を変えるためにwebpack.config.jsを次のように書き換えます。
webpack.config.js
const path = require('path');
const strExpDir = path.resolve(__dirname, 'public');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: strExpDir
},
mode: 'development',
}
この状態にしてwebpack-dev-serverを起動すると、index.jsファイルが変更されると自動的にバンドルしなおすようになります。テストサーバー側のリロードも自動で行われます。
ちなみに、テストサーバーを稼働させない状態で同様のことをしたい場合は、webpack実行時に--watchオプションを加えて実行します。こうすると変更を待機する状態になり、プロンプトは戻ってきません。
準備ができたらhtml-to-imageをインストールします。アプリケーションとして依存があるライブラリとして--saveオプションを使います。
package.jsonには次のように記述が加わります。
...
"dependencies": {
"html-to-image": "^1.11.3"
},
...
index.jsを次のようにします。
index.js
import { toPng } from "html-to-image";
window.htmlToPng = toPng;
ここでは、「 html-to-image 」ライブラリのの「 toPng 」というメソッドをインポートし、windowにセットしています。
こうするのは、モジュールとしてjavaScriptがインポートされるためで例えば、次のようにしてもブラウザのスクリプトタグ内でmytestメソッドは利用できせん。
index.js
...
const mytest=()=> {
console.log('test');
}
index.html
...
<script src="./main.js"></script>
...
<script>
window.onload=()=>{
mytest();
// Uncaught ReferenceError: mytest is not defined
}
</script>
...
mytestメソッドとしてwindowのプロパティにメソッドを設定することにより意図したように利用することができます。
index.js
...
window.mytest=()=> {
console.log('test');
}
html-to-imageに話を戻すと、html側で次のようなコードを書くことで、要素を画像化することができます。
html-to-imageはPromiseタイプの戻り値を返すので、ここでは得られた画像データをそのままimgのsrcに入れて表示しています。
index.html
...
<script>
window.onload = () => {
toPng(document.body).then((png) => {
const img = document.createElement("img");
img.src = png;
document.body.appendChild(img);
});
};
<script>
...
productionとdevelopment
webpack.config.jsに設定するmodeの値についてですが、完成品はproduction、デバッグ時はdevelopmentを指定します。
設定により変更も可能ですが、デフォルトではproductionの時に圧縮してくれます。development時はそのような挙動がないので、エラー箇所の特定が可能になります。
余分なメソッドを排除してくれる機能のことを、WebpackではTree Shakingと言います。詳しい説明はリンク先の公式サイトを確認していただければとおもいますが、この機能が働くのはES2015のモジュール形式で記述したコードに限ります。なので、requireで記述している部分はTree Shakingの機能は働きません。