webpack5設定①

###はじめに

この記事はwebpackの導入未経験の筆者が[Youtube動画【CodeMafia】プログラミング学習 フロントエンド開発(webpack編)](https://www.youtube.com/playlist?list=PLC_Jqbgu7I6baxP0oHxMXtTh7nBGGwqfM)の内容を参考に（動画内webpack4)をwebpack５で実装したことをまとめています。動画内の方が細かい説明もたくさんして下さってますのでぜひ見てみてください。
Babel7に関しては[Babel 7 の主な変更点まとめ](https://qiita.com/soarflat/items/21b8955f992bf7d38581)も参考にさせていただきました。


###開発環境

- Visual Studio Code: v1.56.2
- Node.js: 16.0.0
- Webpack: v5.38.1
- Babel: 7.14.5
- yarn
- npm

[実装ソースコード](https://github.com/yuki-kouno/webpack-lesson)

#Webpackを導入

プロジェクトフォルダを作成してエディターで開たらまずpackage.json初期化

```
yarn init -y    
```

root直下にsrcフォルダを作成し、その中に

- index.html
- imagesフォルダ
- app.js
- sub.js

を作成する

次にwebpackを使えるようにする

```
yarn add webpack webpack webpack-cli --dev
```

root直下にwebpack.config.jsファイルを作成する
 
もしwebpack.example.jsというファイルで使用したい場合は下記のようにconfigオプションでフォルダを指定することで呼び出せる

```
yarn run webpack --mode development --config .\webpack.example.js
```

# エントリーポイントの指定

```js:webpack.config.js
module.exports = {
mode: "development",
  devtool: false,
  entry: "./src/app.js", 
// エントリーポイントが複数ある場合
// お互い依存関係がないファイルは並列に指定
// entry: ["./src/app.js", "./src/sub.js"]

// エントリーポイントも出力先も別で設定したい場合(本記事ではこちらで進めていきます)
 　　entry: {
 　　 app: "./src/app.js",
  　　sub: "./src/sub.js",
  }
}
```

# 出力先の指定

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

module.exports = {
  mode: "development",
  devtool: false,
  entry: "./src/app.js",
  output: {
    path: path.resolve(__dirname, "public"), // __dirnameは現在のディレクトリのパス、第二引数は作成されるフォルダ名
    filename: "bundle.js",
  },
};
```

出力先が複数ある場合は`[name]`という変数で指定する
(変数を指定するとentryで定義したkeyが割り振られる)

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

module.exports = {
  mode: "development",
  devtool: false,
  entry: {
    app: "./src/app.js",
    sub: "./src/sub.js",
  },
  output: {
    path: path.resolve(__dirname, "public"), // __dirnameは現在のディレクトリのパス
    filename: "[name].js",
  },
};
```
これでpublicフォルダにapp.jsとsube.jsを作成されます

#styleローダーの設定

loaderをインストール

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

webpack.config.jsにloaderを設定

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

module.exports = {
  mode: "development",
  devtool: false,
  entry: { app: "./src/app.js" },
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].js",
  },
  module: { // ここから追加
    rules: [
      {
        test: /\.scss$/,
        use: [
          "style-loader", // loaderの記載は下から読まれるので各順番に注意
          "css-loader",
          "sass-loader",
        ],
      },
    ],
  },
};
```

# ベンダープレフィックスの設定
postcss-loader autoprefixerを入れる

```
yarn add postcss-loader autoprefixer --dev
```

root直下にpostcss.config.jsファイル作成して以下を記入

```js:postcss.config.js
module.exports = {
  plugins: [require("autoprefixer")],
};
```

webpack.config.jsにpostcss-loaderを追加

```js:webpack.config.js
...省略  

  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          "style-loader",
          "css-loader",
          'postcss-loader', 　//　追加
          "sass-loader",
        ],
 ...省略     
```

#画像ローダー設定
file-loaderをインストール

```
yarn add file-loader --dev
```

webpack.config.jsにfile-loader設定
[下記で使用している[name]の他に使える変数リスト](https://webpack.js.org/configuration/output/#template-strings)

```js:webpack.config.js
// ...省略
module: {
    rules: [
      {
        test: /\.scss$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
　　　　　　　　　　　　　// ここから
      {
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|ept)$/, // 使用する拡張子を全て記入。webfontも使用されている場合は記入する必要がある
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name].[ext]",　// [name]を変えることでpublicPahをhashにしたりできる
              outputPath: "images",
              publicPath: "images",　// developer toolsのstyle などで表示されるurl
            },
          },
        ],
      },
      // ここまで
// ...省略
```

#Babel設定
babelをインストール

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

webpack.config.jsにbabel-loader設定

```js:webpack.config.js
// ...省略
  module: {
    rules: [
      // ここから
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/, // node_moduleは元からトランスパイルされているので除外
      },
　　　　　　　　　　　　　// ここまで
      {
        test: /\.scss$/,
        use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
      },
// ...省略
```

root直下に.babelrcファイル作成して以下を記入(jsで書く場合はbabelrcファイルは作成しない)

```json:.babelrc
{
    "presets": ["@babel/preset-env"]
}
```

.babelrcではなくjsファイルで書く場合(こっちの方が関数を使った複雑な設定ができる）
root直下にbabel.config.js作成

```js:babel.config.js
module.exports = (api) => {
  api.cache(true);   // 設定しておくと2回目以降キャッシュが走ってビルド時間短縮できる

  return {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: [   　　　　　　　　　　       　　// 対応させるブラウザの指定
            "last 1 version",　　　　   　　　// 指定の仕方(https://github.com/browserslist/browserslist)
            "> 1%",
            "maintained node versions",
            "not dead",
          ],
        },
      ],
    ],
  };
};
```

トランスパイルを行う際に古いブラウザが持っていない機能を補う必要があるため以下のモジュール追加

```
yarn add core-js@3 regenerator-runtime
```

上記のモジュールは直接importして使うとファイルサイズが大きくパフォーマンスが悪くなるのでbabel.config.jsで以下のように設定

```js:babel.config.js
module.exports = (api) => {
  api.cache(true);

  return {

    presets: [
      [
        "@babel/preset-env",
        {
          targets: [
            "last 1 version",
            "> 1%",
            "maintained node versions",
            "not dead",
          ],
          useBuiltIns: "usage", // 追加　regenerator-runtimeの必要な機能だけimportする
          corejs: { version: 3, proposals: true },　// 追加
        },
      ],
    ],
  };
};
```

# Eslint設定

loaderをインストール
(以下、説明のためスクラッチで書いていくが `yarn run eslint --init` を使う方法もある)

```
yarn add eslint eslint-loader babel-eslint --dev
```

root直下に.eslintrc作成し以下を記入

```json:.eslintrc
{
    "env": {　// env設定ドキュメント(https://eslint.org/docs/user-guide/configuring/#specifying-environments)
        "browser": true,
        "es2017": true
    },
    "extends": "eslint:recommended", // recommendedはeslintがおすすめしているルールが自動で適用
    "parser": "babel-eslint",
    "parserOptions": {
        "sourceType": "module" // es6で導入されたmodule単位で管理
                               // defaltではscript管理になっている
    },
    "globals": {   // globalから使用されるpackageを記入
         "jQuery": "readonly",
　　　　　　　　　　　　　　　　 "$": "readonly"
    },
    "rules": {
        // ここに記入したルールが優先される
        // ESLintルールリスト(https://eslint.org/docs/rules/)
    }
}
```

webpack.config.jsにloaderの設定

```js:webpack.config.js
// ...省略
module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
       //　ここから追加
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true, // lintに引っかかったところを自動でルールの形に整形する
        },
      },
      //　ここまで
// ...省略
```

#Pluginを使う
###MiniCssExtractPluginを設定

MiniCssExtractPluginはjsファイルをバンドルする際にcssを分離して生成することで、変更がない場合はキャッシュを利用してくれる

MiniCssExtractPluginをinstall

```
yarn add mini-css-extract-plugin --dev
```

webpack.config.jsに追加

```js:webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 追加

module.exports = {
  mode: "development",
  devtool: false,
  entry: {
    app: "./src/app.js",
    sub: "./src/sub.js",
  },
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          fix: true,
        },
      },
      {
        test: /\.scss$/,
        use: [
          // "style-loader",　<- 削除
          MiniCssExtractPlugin.loader, // 追加
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
      {
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|ept)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name].[ext]",
              outputPath: "images",
              publicPath: "images",
            },
          },
        ],
      },
    ],
  },
  // ここから追加
  plugins: [
    new MiniCssExtractPlugin({
      filename: `[name].css`,
    }),
  ],
  // ここまで
};

```

###HtmlWebpackPluginを設定
HtmlWebpackPluginはindex.htmlに自動でscriptタグをインジェクトする

html-webpack-pluginをinstall

```
yarn add html-webpack-plugin --dev
```

webpack.config.jsに追加

```js:webpack.cofig.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 追加

// ...省略

  plugins: [
    new MiniCssExtractPlugin({
      filename: `[name].css`,
    }),
    // ここから追加
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      inject: "body",
    }),
    //ここまで
  ],
};
```

###HTML内の画像を抽出する
html内の画像もバンドルするためにhtml-loaderを設定する
html-loaderは先ほど設定したHtmlWebpackPluginがないと機能しません。

loaderをinstall

```
yarn add --dev html-loader
```

webpack.config.jsにhtml-loader追加

```js:webpack.config.js
// ...省略
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|ept)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name].[ext]",
              outputPath: "images",
              publicPath: "images",
            },
          },
        ],
      },
      // ここから追加
      {
        test: /\.html$/,
        use: ["html-loader"],
      },
      // ここまで
    ],
// ...省略
```

[webpack５設定②](https://qiita.com/zgoovw_/items/7d964694a406f39a9739)に続く