webpack + jQuery(webpack.config.js書き方の例)

# webpack + jQuery
自分用メモ

# webpackはモジュールバンドラー

エントリーポイントに設定したjsファイルを中心に、各モジュールのjsファイルを纏めてbundle

- モジュール管理できるため機能ごとに開発ができ保守性が上がる
- 複数のjsファイル(cssや画像も)を一つのファイルにbundleできるためリクエスト数減少とパフォーマンス向上
※ ファイルサイズの増大によるパフォーマンス低下もありうる
- 依存関係の解消

CSSをインラインのstyleではなく、CSSファイルとして出力

# 1. webpack使用の下準備
## 1.1. webpackでの基本的なパッケージのインストール

```shell:
$ npm init -y
$ npm i -S jquery
$ npm i -D webpack webpack-cli 
$ npm i -D terser-webpack-plugin optimize-css-assets-webpack-plugin mini-css-extract-plugin
$ npm i -D node-sass css-loader sass-loader style-loader postcss-loader
$ npm i -D babel-loader @babel/core @babel/polyfill @babel/preset-env 
$ npm i -D autoprefixer

# 分割したcssの読み込みをリロードしない(brouser-sinc代用)
$ npm i -D webpack-dev-server

# gulp使うとき
$ npm i -D gulp gulp-eslint gulp-notify gulp-plumber
```

## 1.2. package.jsonの編集

```diff_json:package.json
{
  "name": "webpack_test",
  "version": "1.0.0",
  "description": "",
- "main": "index.js",
  "scripts": {
-   "test": "echo \"Error: no test specified\" && exit 1",
    // sassも監視対象
+   "build:dev": "webpack --mode development --watch",
+   "build": "webpack --mode production",
    // dev-serverだけだとjsの監視はされるが、バンドルはされない
    // 保存すると自動リロードがかかるため更新はされるが、監視よりも遅いため二回目リロード時にバンドルが反映される
+   "start": "webpack --mode development --watch & webpack-dev-server",
  },
    "scripts": {
    },
+ "private": true,
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "jquery": "^3.4.1"
  },
  "devDependencies": {
    "@babel/core": "^7.4.5",
    "@babel/polyfill": "^7.4.4",
    "@babel/preset-env": "^7.4.5",
    "autoprefixer": "^9.6.0",
    "babel-loader": "^8.0.6",
    "css-loader": "^3.0.0",
    "mini-css-extract-plugin": "^0.7.0",
    "node-sass": "^4.12.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "terser-webpack-plugin": "^1.3.0",
    "webpack": "^4.35.2",
    "webpack-cli": "^3.3.5",
    "webpack-dev-server": "^3.7.2"
  }
}
```

## 1.2. webpack.config.jsの作成
webpackをインストールしたら`webpack.config.js`を手動で作成


```javascript:webpack.config.js
// 開発or本番モードの選択(development、production、noneのいずれか設定必須)
// development: 開発時のファイル出力のモード(最適化より時間短縮,エラー表示などを優先)
// production: 本番時のファイル出力のモード(最適化されて出力される)
const MODE = "development";
// ソースマップの利用有無(productionのときはソースマップを利用しない)
const enabledSourceMap = MODE === "development";

// ファイル出力時の絶対パス指定に使用
const path = require('path');

// プラグイン
// js最適化
const TerserPlugin = require('terser-webpack-plugin');
// css最適化
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// css抽出
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// jQueryで使用
const webpack = require('webpack');


module.exports = {
  // エントリーポイント(メインのjsファイル)
  entry: './src/js/app.js',
  // ファイルの出力設定
  output: {
    // 出力先(絶対パスでの指定必須)
    path: path.resolve(__dirname, 'dist/js'),
    // 出力ファイル名
    filename: "bundle.js"
  },
  mode: MODE,
  // ソースマップ有効
  devtool: 'source-map',
  // ローダーの設定
  module: {
    rules: [
      {
        // ローダーの対象 // 拡張子 .js の場合
        test: /\.js$/,
        // ローダーの処理対象から外すディレクトリ
        exclude: /node_modules/,
        use: [
          {
            // Babel を利用する
            loader: "babel-loader",
            // Babel のオプションを指定する
            options: {
              presets: [
                // プリセットを指定することで、ES2019 を ES5 に変換
                "@babel/preset-env"
              ]
            }
          }
        ]
      },
      {
        // ローダーの対象 // 拡張子 .scss の場合
        test: /\.scss/,
        // Sassファイルの読み込みとコンパイル
        use: [
          // linkタグに出力する機能
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // css出力先を指定
              publicPath: path.resolve(__dirname, 'dist/css'),
              // developmentのときのみ有効
              hmr: process.env.NODE_ENV === 'development',
            },
          },
          // CSSをバンドルするための機能
          {
            loader: "css-loader",
            options: {
              // CSS内のurl()メソッドの取り込みを禁止する
              // 画像ファイルをbase64でエンコードするとかでは無い限り、必要なし
              url: false,
              // ソースマップの利用有無
              sourceMap: enabledSourceMap,
              // Sass+PostCSSの場合は2を指定
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
              importLoaders: 2
            }
          },
          // PostCSS(Autoprefixer)のための設定
          // ベンダープレフィックスを追加するためのPostCSS用プラグイン
          {
            loader: "postcss-loader",
            options: {
              // PostCSS側でもソースマップを有効にする
              sourceMap: enabledSourceMap,
              plugins: [
                // Autoprefixerを有効化
                require("autoprefixer")({
                  grid: true
                })
              ]
            }
          },
          // Sassをバンドルするための機能
          {
            loader: "sass-loader",
            options: {
              // ソースマップの利用有無
              sourceMap: enabledSourceMap
            }
          }
        ]
      }
    ]
  },
  // mode:puroductionでビルドした場合のファイル圧縮
  optimization: {
    minimizer: production
      ? []
      : [
        // jsファイルの最適化
        new TerserPlugin({
          // すべてのコメント削除
          extractComments: 'all',
          // console.logの出力除去
          terserOptions: {
            compress: { drop_console: true }
          },
        }),
        // 抽出したcssファイルの最適化
        new OptimizeCssAssetsPlugin({})
      ]
  },
  // js, css, html更新時自動的にブラウザをリロード
  devServer: {
    // サーバーの起点ディレクトリ
    // contentBase: "dist",
    // バンドルされるファイルの監視 // パスがサーバー起点と異なる場合に設定
    publicPath: '/dist/js/',
    //コンテンツの変更監視をする
    watchContentBase: true,
    // 実行時(サーバー起動時)ブラウザ自動起動
    open: true,
    // 自動で指定したページを開く
    openPage: "index.html",
    //　同一network内からのアクセス可能に
    host: "0.0.0.0"
  },
  plugins: [
    // 抽出したCSSファイル最適化
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.optimize\.css$/g,
      cssProcessor: require('cssnano'),
      cssProcessorPluginOptions: {
        preset: ['default', { discardComments: { removeAll: true } }],
      },
      canPrint: true
    }),
    // ビルドされたjsファイルからstyleの部分を抽出してcssファイルで出力
    // jsファイルからcssの内容消える
    // v3でのextract-text-pluginのようなもの
    new MiniCssExtractPlugin({
      // 出力先ファイル名 // prefix は output.path
      filename: "../css/[name].css",
      chunkFilename: "[id].css"
    }),
    new webpack.ProvidePlugin({
      $: 'jquery'
    })
  ]
};
```

# 2. webpackの処理対象となるjsファイルの書き方
エントリーポイントとなるjsファイルを`app.js`モジュールファイルを`module1.js`とすると次のように書く

```javascript:app.js
import "../scss/style.scss";
import jQuery from 'jquery';
import "@babel/polyfill";
import { モジュールから呼び出す関数 } from './modules/module1';
```


```javascript:module.js
// 関数を定義
```

```html:index.html
<!DOCTYPE html>
<html lang="ja" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>webpack_sample</title>
  <link rel="stylesheet" href="./dist/css/main.css">
</head>
<body> 
  // 
  <script src="./dist/js/bundle.js"></script>
</body>

</html>

```

# 3. webpack実行

実行コマンド

`package.json`の`scripts`で設定した名称で実行

```shell
$ npm run build:dev  # バンドル(開発用)実行
$ npm run build  # バンドル(本番用)実行
$ npm run start  # バンドルと監視スタート
```