【webpack】webpack設定を一からまとめてみる①

## webpackとは
フロントエンド開発におけるオープンソースのモジュールバンドラーである。
ここで言うモジュールとは、JavascriptやCSS、画像などのファイルを示し、モジュールバンドラーとはこれらのファイルをまとめる役割を持つ。現在の最新はバージョン5となっている。
![スクリーンショット 2020-11-08 11.20.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531606/f631e34a-5ff7-b66f-a1d9-5f6d2af03c1d.png)
webpackを利用するメリット
・自動でファイルの依存関係を解決してくれる
・リソースをコンパクトにしてくれる
・ローダーや各種プラグインを導入する事でデフォルトの設定を上書きし、機能を拡張する事が出来る

## 導入方法
####①Node.jsをインストールする
webpackはNode.jsで動かす為、最初にインストールしておく。
####②フロントエンドパッケージを初期化
Node.js上で動かすモジュールの管理には今回npmを使用する。

```
npm init -y 
```
上記コマンドを実行すると、対象プロジェクトを管理するpackage.jsonが作成される。

####③webpackをインストールする

```
npm install --save-dev webpack webpack-cli
```
上記は対象プロジェクト内に適用する場合である。
PC全体に適用した場合はグローバルでインストールすれば良い。

####④webpackを実行する

```
npx webpack
```
上記コマンドでwebpackを実行し、バンドルを開始出来る。


## 基本設定
開発環境、本番環境で実行結果を分岐させたりなど、
設定ファイルにオプションを指定してやるのが一般的な使い方となっている。
また設定ファイルは、CommonJSの規格でモジュールをインポート・エクスポートする仕様となっている。

```webpack.config.js
module.exports = {
    mode: 'development', // 開発モードか本番モードかを設定出来る
    entry: '.src/app.js' // バンドルの起点となるファイル
}
```
デフォルトでdevelopmentモードとproductionモードが存在し、
バンドルされるファイルの圧縮についての設定が変わるなど、モード設定だけで使用が可能。
参考：[Mode | webpack](https://webpack.js.org/configuration/mode/#mode-development)

entryはバンドルの起点となるファイルを設定する事が出来て、
ファイルの依存関係を解決しながらバンドルされる。

エントリーポイントは配列で複数指定する事も可能であり、
それぞれ依存関係がないファイルも1つのファイルにバンドルする事が出来る。

```webpack.config.js
module.exports = {
    mode: 'development',
    entry: ['.src/app.js', '.src/foo.js'] // エントリーポイントは複数設定可
}
```
また、依存関係のないファイルをそれぞれ別ファイルで出力する場合はオブジェクト形式で設定すれば良い。

```webpack.config.js
module.exports = {
  mode: 'development',
  entry: {
       app: '.src/app.js',
       foo:  '.src/foo.js'
  }
}
```

出力先のディレクト、ファイル名を指定する時はoutputプロパティを使用する。
特に指定しなければ、distディレクトにmain.jsが出力される初期設定になっている。

```webpack.config.js
const path = require('path'); // pathモジュールを使用する

module.exports = {
  mode: 'development',
  entry: {app: './src/app.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'), // 出力されるディレクトリの指定
    filename: 'bundle.js' // 出力されるファイル名の指定
  },
}
```

ディレクトリを絶対パスで指定する必要がある為、pathモジュールをrequireしている。


```webpack.config.js
const path = require('path'); 

module.exports = {
  mode: 'development',
  entry: {app: './src/app.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'), 
    filename: '[name].bundle.js' // name、idなど変数を含める事が出来る
  },
}
```
[name]を指定すると、entryをオブジェクト指定した際のキーが入る仕様となっている。

## ローダーの導入
webpackでjavascriptを扱う分には初期設定でも問題ないが、
違う拡張子のファイルも扱う場合にはローダーを導入する必要が出てくる。

### sassファイルをバンドルする
以下のコマンドで必要なローダーをnpmでインストールする。

```
npm add --dev sass sass-loader css-loader style-loader
```

configファイルの設定。余談だが、\はmacではoption + ¥で入力可能。

```webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: {app: './src/app.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: '[name].bundle.js'
  },
  module: { // モジュールプロパティの中に
    rules: [ // ルールズを配列で指定
      {
        test: /\.scss$/, // 対象の拡張子を正規表現で指定。ちなみに$は末尾を表している
        use: [// どのローダーを使うのか（下から実行されていく）
          'style-loader', //cssをhtmlにstyleタグとして出力
          'css-loader', // jsにバンドルされる
          'sass-loader' // cssに変換、コンパイルする
        ]
      }
    ]
  }
}
```

使用するローダーは下から順番に実行されると言う点に注意する。

### 画像ファイルをバンドルする

cssからbackground-imageなどで画像を読み込む場合、これもバンドルの対象となるが、
このようなケースでも、別途ローダーを導入する必要がある。

```
npm add --dev file-loader
```

configファイルの設定。
WEBフォントも画像と同様にfile-loaderを介す必要がある。

```webpack.config.js
const path = require('path');

module.exports = {
  mode: 'production',
  entry: {app: './src/app.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|eot)$/, // 拡張子毎に設定
        use: [
          { // ローダーオプションを使用する際はオブジェクト形式
            loader: 'file-loader',
            options: {
              name: '[contenthash].[ext]', // 拡張子エクステンションのext
              outputPath: 'images', // 格納するディレクトリ
              publicPath: 'images', // サーバー上のパス情報。CDNなどで利用する
            }
          }
        ]
      }
    ]
  }
}
```

nameに画像ファイル名を指定出来て、ここでも[name]、[id]などが指定可能。
[contenthash]とする事で本番でのキャッシュ対策にファイル名をハッシュ値で指定する事が出来る。

ハッシュ値に設定についてはいくつか選択肢がある
[hash]・・・ビルド単位で発行されるハッシュ。あまり使わない。
[contenthash]・・・コンテンツ単位で発行されるハッシュ。画像ファイルで良く使う。
[chunkhash]・・・チャンクファイル単位で発行されるハッシュ。具体的には1つのoutputのバンドル単位でチャンクが構成される。

参考：[Output | webpack](https://webpack.js.org/configuration/output/)

### babelを導入する
ES2015以降で書かれたコードをES5以前の環境でも動作するようにする為、babelと連携させる。以下のコマンドで必要なローダーと合わせてインストールする。

```
npm add --dev babel-loader @babel/core @babel/preset-env   
```

configファイルの設定。babel-loaderを噛ます記述を追加する。

```webpack.config.js
const path = require('path');

module.exports = {
  mode: 'production',
  entry: {app: './src/app.js', sub: './src/sub.js'}, 
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/, // node_modulesはbabelを通す必要がない
        use: [
          'babel-loader',
        ]
      },
    ]
  }
}
```

babel用の設定ファイルも作成する。

```json:.babelrc
{
  "presets": ["@babel/preset-env"]
}
```
これでES2015以降で書かれたコードもES5以前のコードへ変換される。

## ESLintの導入
ESLintは、JavaScriptのコードで見つかった問題パターンを識別する為の、静的コード分析ツールである。ルール設定が柔軟であり、Google社が公開しているeslint-config-googleなどが有名である。

以下のコマンドで必要なローダーと合わせてインストールする。

```
npm add eslint eslint-loader babel-eslint --dev   
```

babel-loaderの前にeslint-loaderを使用するよう記述を追加する。

```webpack.config.js

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/, // babelを通す必要がない
        use: [
          'babel-loader',
          'eslint-loader'
        ]
      },
    ]
  }

```


ESlint設定ファイルを作成する。

```json:.eslintrc
{
  "env": {
    "browser": true,
    "es2017": true,
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint"
}
```

取り敢えず、基本的な設定はここまで。
ESlintのオプションやルール設定の詳細などはまた別の記事でまとめる事とする。
SplitChunksなど応用的な使用方法などについても、長くなってしまったので、次回以降で。