Webpackについて理解を深める(その1)
どうもoreoです。
何回かに分けてWebpackについて記載していきたいと思います。今回はそもそもWebpackとは?というところから、基本的な設定について触ります。この記事で基本を抑え、続編ではWebpackのarchitectureやconceptなどを深掘りしたいと思います。
1 Webpackとは?
Webpackは複数のJavaScriptファイルを一つのファイルにまとめて出力するツールです。この「一つのファイルにまとめる」ことをバンドル(bundle)と言い、また「モジュールを一つのファイルにまとめて出力するツール」のことをモジュールバンドラー(module bundler)と呼びます。
公式DocumentでWebpackは、👇のように静的モジュールバンドラーと紹介されています。
webpack is a static module bundler for modern JavaScript applications
公式には👇のような図が記載されています。バンドルの雰囲気がわかりやすいですね。
この図には、JavaScript以外のファイルも記載されています。Lodaerと呼ばれる機能を使えば、CSSファイルなどもバンドル可能です。また、設定次第で複数ファイルにバンドルすることも可能です。
2 なぜWebpackを使うのか?
複数のJavaScriptファイルを一つにまとめることで、ブラウザからのリクエスト数を減らし、ファイル転送の最適化が可能です。これがWebpackなどのモジュールバンドラーを使う大きな理由です。
JavaScriptファイルが複数存在することでリクエスト回数が増えて転送効率が落ちるのであれは、最初から一つのファイルに記載することも可能ですが、可読性や保守性などの観点から現実的でありません。また、機能を複数ファイルに分割したままだと、ブラウザからのリクエスト回数が増えてファイル転送効率が落ちます。こういったつらみに対して、Webpackを利用することで機能をファイルごとに分割しながら開発し、実行時は一つのファイルとしてブラウザに提供することが可能となります。
👇の記事では、devツールで実際のファイルサイズを細かく見ながら、Webpackのメリットについて言及されていますので、参考までに。
3 Webpackを実際に触る
3-1 環境
node:16.3.0
yarn:1.22.19
webpack:5.74.0
3-2 webpackのインストール
任意のディレクトリでプロジェクトを作成します。-y
オプションをつけると、package.json
に記載されるname
、version
の設定などがデフォルト設定となります。
yarn init -y
webpackをインストールします。--dev
または-D
オプションをつけることで、devDependenciesにパッケージを追加できます。
yarn add webpack webpack-cli --dev
devDependenciesについては👇ご参照。
3-3 サンプルファイルの作成
srcディレクトリを作成し、index.js
とsub.js
を作成します。
├── node_modules
├── package.json
├── src
│ ├── index.js
│ └── sub.js
└── yarn.lock
index.js
import { sayBow } from "./sub"
sayBow()
sub.js
export function sayBow() {
console.log("bow!")
}
3-4 バンドルしてみる
yarn webpack
👆のコマンドでバンドルすると、index.js
とsub.js
が一つのmain.js
というファイルにまとめられ、dist
フォルダ配下に出力されます。
├── dist
│ └── main.js
├── node_modules
├── package.json
├── src
│ ├── index.js
│ └── sub.js
└── yarn.lock
3-5 webpackの設定をカスタマイズする
webpack.config.js
でwebpackの各種設定が可能です
エントリーポイントと出力先の設定
エントリーポイントつまりバンドルの起点となるファイルは、entry
で設定できます。下記例では、./src/index.js
を起点にバンドルされます。
バンドルファイルの出力先は、output
で設定できます。path
で出力先のディレクトリを指定し、filename
で出力ファイル名を設定できます。path.resolve(__dirname,'public')
では、ルートパス/public
という絶対パスが返されます。
webpack.config.js
const path = require("path")
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname,'public'),
filename: "main.js"
},
};
このような設定だと、public
フォルダ配下にバンドルファイルが出力されます。
├── public
│ └── main.js
├── node_modules
├── package.json
├── src
│ ├── index.js
│ └── sub.js
└── yarn.lock
ここでpublic
フォルダ配下にindex.html
を追加してバンドルファイルを読み込んでみます。
├── public
│ ├── index.html
│ └── main.js
├── node_modules
├── package.json
├── src
└── yarn.lock
public/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>テスト</h1>
<div class="bg"></div>
<script src="./main.js"></script>
</body>
</html>
index.html
を確認するとバンドルファイルが読み込まれていることが確認できます。
modeオプションで環境を使い分ける
mode
オプションで、production環境とdevelopment環境の使い分けが可能です。
module.exports = {
・
・
・
mode: "development"
};
developmentでバンドルした場合、バンドルファイルには下記のようにソースマップが出力され、デバックが簡単になります。
一方で、productionでバンドルした場合は、下記のように圧縮されてファイル容量が小さくなります。
通常は環境ごとにwebpackの設定ファイルを用意して使用します。
devサーバーを使う
devサーバーを設定すると、HMR(Hot Module Replacement)が有効となります。HMRとは、ソースコード内のCSS/JSに変更があった場合にフルリロードすることなく、差分をブラウザに適用してくれる機能です。
webpack-dev-serverをインストールします。
yarn add webpack-dev-server --dev
webpack.config.js
でdevServer
を設定します。open
をtrue
にすることで、devサーバーを立ち上げると自動でブラウザが立ち上がるようになります。static
にdevサーバーで表示するリソースのディレクトリを指定します。
module.exports = {
・
・
・
devServer: {
open: true,
static: path.join(__dirname, "public"),
},
};
👇のコマンドで、devサーバーを起動できます。
yarn webpack serve
scssをバンドルできるようにする
JavaScript以外のファイルをバンドルする際は、loaderをインストールする必要があります。
ここでは、sass
そのもの、sassをバンドルするsass-loader
、cssをバンドルするcss-loader
、style要素としてHTMLにcssを注入するstyle-loader
をインストールします。
yarn add --dev sass sass-loader css-loader style-loader
webpack.config.js
に、loaderを設定する際は、module
を追加します。rules
プロパティの配列にオブジェクトの形でloaderの設定を記載します。test
には、対象ファイルを記載し、use
には使用するloaderを記載します。use
に記載するloaderは下から順番に実行されるので、実行させたい順番に記載しなければならない点、注意が必要です。
module.exports = {
・
・
・
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
]
}
};
index.scss
をsrc
フォルダ配下に作成して、index.js
に読み込ませます。
├── src
├── index.js
├── index.scss
└── sub.js
index.scss
h1{
text-decoration:underline;
}
index.js
import { sayBow } from "./sub"
import "./index.scss"
sayBow()
devサーバーを起動すると、underlineが追加されており、scssが有効になっていることがわかります。devtoolのElementsタグを確認すると、head
要素のstyle
要素にcssが注入されていることがわかります。
画像をバンドルできるようにする
画像をバンドルするには、webpack4まではfile-loaderをインストールする必要がありましたが、webpack5ではネイティブサポートのAsset Modulesで対応が可能となりました。
src/images/dog.png
を追加
├── src
├── images
│ └── dog.png
├── index.js
├── index.scss
└── sub.js
index.scss
で画像ファイルを表示するようにする。
h1{
text-decoration:underline;
}
.bg{
height: 100px;
width: 100px;
background-image: url('./images/dog.png');
background-size: cover;
}
webpack.config.js
での設定の追加。test
にはバンドル対象のファイル拡張子、generator
には、バンドルファイルの出力したいディレクトリを設定します。
module.exports = {
・
・
・
module: {
rules: [
・
・
・
{
test: /\.png$/i,
generator: {
filename: `./image/[name][ext]`,
},
}
]
}
};
バンドルしてdevサーバーを立ち上げると、画像が反映されています。
マルチエントリーポイント対応をする
マルチエントリーポイントに対応したい場合は、entry
で配列の形でエントリーポイントを渡しますと、一つのファイルにバンドルできます。
module.exports = {
entry: ["./src/index.js","./src/sub.js"],
・
・
・
};
マルチエントリーポイントで、出力ファイルを分けたい場合は、entry
にオブジェクトの形で、エントリーポイントを渡します。また、output
に渡すファイル名を[name]
とするとentry
オブジェクトに渡したキーが自動でファイル名に補完されバンドルファイルが出力されます。
module.exports = {
entry: {
index: "./src/index.js",
sub: "./src/sub.js"
},
output: {
path: path.resolve(__dirname,'public'),
filename: "[name].js"
},
・
・
・
};
バンドルすると以下のように、index.js
とsub.js
という形で出力されます。
├── public
├── image
├── index.html
├── index.js
└── sub.js
public/index.html
のscript
の読み込みを./index.js
に変更するとバンドルファイルの読み込みをブラウザで確認できます。
エイリアスを設定する
resolve
にalias
を設定するとpathのエイリアスを設定できます。以下では、src/images
のpathに対して@
というエイリアスが設定ができます。
module.exports = {
・
・
・
resolve: {
alias: {
"@" :path.resolve(__dirname,"src/images")
}
}
};
index.scss
のbackground-image
でエイリアスが使用できます。
h1{
text-decoration:underline;
}
.bg{
height: 100px;
width: 100px;
background-image: url('@/dog.png');
background-size: cover;
}
4 最後に
今回はWebpackの基本的な部分についてまとめてみました。環境ごとの設定ファイルの分割、最適化手法などさらに細かい設定が可能ですので、気になる方は公式などをご覧ください。
続編では、設定方法の深掘りというよりかは、architectureの深掘りをしたいと思います!
5 参考
Discussion