LoginSignup
3
4

More than 3 years have passed since last update.

Webpack5 設定②

Posted at

この記事について

この記事はWebpack5 設定①の続きです。

webpackを分割

webpackを共有、商用、開発用に分割します。

  • webpack.config.jsのファイル名をwebpack.common.jsに変更
  • webpack.common.jsをコピーしてファイル名をwebpack.dev.jsに変更
  • 同じくもう一つwebpack.common.jsをコピーしてファイル名をwebpack.prod.jsに変更
  • それぞれ中身を以下のように変更
webpack.common.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = ({ outputFile, assetFile }) => ({
  entry: {
    app: "./src/app.js",
    sub: "./src/sub.js",
  },
  output: {
    path: path.resolve(__dirname, "public"),
    filename: `${outputFile}.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: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
      {
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|ept)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: `${assetFile}.[ext]`,
              outputPath: "images",
              publicPath: "images",
            },
          },
        ],
      },
      {
        test: /\.html$/,
        use: ["html-loader"],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: `${outputFile}.css`,
    }),
  ],
});

webpack.dev.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name]";
const assetFile = "[name]";

module.exports = () =>
  webpackMerge(commonConf({ outputFile, assetFile }), {
    mode: "development",
    devtool: "source-map",
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        inject: "body",
      }),
    ],
  });
webpack.prod.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name].[chunkhash]";
const assetFile = "[name].[contenthash]";

module.exports = () =>
  webpackMerge(commonConf({ outputFile, assetFile }), {
    mode: "production",
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        inject: "body",
      }),
    ],
  });

bundleして作られたファイルを自動で消すために以下を追加

yarn add rimraf --dev

webpack-mergeも追加

yarn add webpack-merge --dev

package.jsonにscripts追加

package.json
{
  "name": "webpack-lesson",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  // ここから追加
  "scripts": {
    "cleanup": "npx rimraf ./public", 
    "dev": "npm run cleanup && npx webpack --config ./webpack.dev.js",
    "build": "npm run cleanup && npx webpack --config ./webpack.prod.js"
  },
  // ここまで
  "devDependencies": {
   // ...省略
}

ローカルパッケージを実行するときはnpxでscriptsのコマンドはnpm run

ファイルをミニファイ化する

jsのミニファイプラグイン(terser-webpack-plugin)とcssのミニファイプラグイン(optimize-css-assets-webpack-plugin, html-webpack-plugin)を追加

html-webpack-pluginはすでに入れてあるので他の残りをinstall

yarn add optimize-css-assets-webpack-plugin terser-webpack-plugin --dev

webpack.prod.jsに追加

webpack.prod.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const OptimizeCssPlugin = require("optimize-css-assets-webpack-plugin"); // 追加
const TerserPlugin = require("terser-webpack-plugin"); // 追加
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name].[chunkhash]";
const assetFile = "[name].[contenthash]";

module.exports = () =>
  webpackMerge(commonConf({ outputFile, assetFile }), {
    mode: "production",
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        inject: "body",
        // ここから追加
        minify: {
          collapseWhitespace: true,
          keepClosingSlash: true,
          removeComments: true,
          removeRedundantAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true,
          useShortDoctype: true,
        },
        // ここまで
      }),
    ],
    // ここから追加
    optimization: {
      minimizer: [new TerserPlugin(), new OptimizeCssPlugin()],
    },
    // ここまで
  });

各プラグインについてのリンク
- webpackのmode
- Optimize CSS Assets Webpack Plugin
- webpackのminification
- terser-webpack-plugin

開発サーバー設定

webpackのサーバー追加

yarn add webpack-dev-server --dev

webpack.dev.jsに設定
webpadk dev-serverページ

webpack.dev.js
// ...省略
module.exports = () =>
  webpackMerge(commonConf({ outputFile, assetFile }), {
    mode: "development",
    devtool: "source-map",
    // ここから追加
    devServer: {
      open: true,
      contentBase: "./public",
      watchOptions: {
        ignored: /node_modules/,
      },
    },
    // ここまで
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        inject: "body",
      }),
    ],
  });

scriptsの追加と変更

package.json
{
  "name": "webpack-lesson",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "cleanup": "npx rimraf ./public",
    "dev": "npm run webpack:dev && npm run webpack:server",
    "webpack:server": "npx webpack serve --config ./webpack.dev.js",
    "webpack:dev": "npm run cleanup && npx webpack --config ./webpack.dev.js",
    "build": "npm run cleanup && npx webpack --config ./webpack.prod.js"
  },
  "devDependencies": {
// ...省略

共有プラグイン設定

jQueryを例に、使用場所ごとにimportしなくても使えるように設定する

webpack.common.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { ProvidePlugin } = require("webpack"); // 追加

// ...省略

 plugins: [
    new MiniCssExtractPlugin({
      filename: `${outputFile}.css`,
    }),
    // ここから追加
    new ProvidePlugin({
      jQuery: "jquery",
      $: "jquery",
    }),
    // ここまで
  ],
});

.eslintrcにも追加

.eslintrc
{
    "env": {
        "browser": true,
        "es2017": true,
        "jquery": true // 追加
    },
// ...省略

SplitChunksを設定

一つ前のセクションでjQueryの共有プラグイン設定をしましたが、現状ではapp.jsでsub.jsそれぞれでjQueryを使用してる場合、jQueryを2回読み込んでることになります。またそれぞれのjsファイルに変更があった場合にjQueryを含んだキャッシュが生成されているので変更があるたびにjQueryも再配信されることになります。
そうした無駄を無くすためにバンドル時にjQueryを別で生成して、他のjsファイルに変更があった場合にjQueryは再配信しなくても済むように処理をします。

webpack optimization.splitChunksページ

jQueryのようなサードパーティーライブラリの登録

webpack.common.jsにSplitChunksを設定

webpack.common.js
// ...省略
  output: {
    path: path.resolve(__dirname, "public"),
    filename: `${outputFile}.js`,
    chuncFilename: `${outputFile}.js`, // 追加
  },
// ...省略
plugins: [
    new MiniCssExtractPlugin({
      filename: `${outputFile}.css`,
    }),
    new ProvidePlugin({
      jQuery: "jquery",
      $: "jquery",
    }),
  ],
  // ここから追加
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0,
      cacheGroups: {
        vendor: {
          name: "vendors",
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        default: false,
      },
    },
    // ここまで
  },
});

独自の共通部品の登録

次の例ではsrc直下にutilsフォルダ作成して、その中にindex.jsを作成し、
以下のようなfunctionをExportして、それぞれのjsファイルでimportせずに使いまわせるようにする

index.js
export default {
  log: function (str) {
    console.log(str);
  },
};

webpack.common.jsに以下を追加

webpack.common.js
// ...省略
  plugins: [
    new MiniCssExtractPlugin({
      filename: `${outputFile}.css`,
    }),
    new ProvidePlugin({
      jQuery: "jquery",
      $: "jquery",
      utils: [path.resolve(__dirname, "src/utils"), "default"], // 追加 指定したpathでexportしているdefault関数を指定している
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0,
      cacheGroups: {
        vendors: {
          name: "vendors",
          test: /node_modules/,
          priority: -10,
        },
        // ここから追加
        utils: {
          name: "utils",
          test: /src[\\/]utils/,
        },
        // ここまで
        default: false,
      },
    },
  },
});

.eslintrcにutils追加

.eslintrc
{
    "env": {
        "browser": true,
        "es2017": true
    },
    "extends": "eslint:recommended",
    "parser": "babel-eslint",
    "parserOptions": {
        "sourceType": "module" 
    },
    "globals": {
        "utils": "readonly", // 追加
        "jQuery": "readonly",
        "$": "readonly"
    },
    "rules": {
    }
}

Resolveを使う

ツリー構造を整理してresolveを設定することでpathの最適化をします。

src直下にjsフォルダを作成して、その中にutilsを移動
app.jsとsub.jsもjsフォルダに移動

scssフォルダを作成して、その中にapp.scssを移動

ファイルの移動に合わせてpathを変更

webpack.common.js
// ...省略
module.exports = ({ outputFile, assetFile }) => ({
  entry: {
    app: "./src/js/app.js", // 変更
    sub: "./src/js/sub.js", // 変更
  },
// ...省略
new ProvidePlugin({
      jQuery: "jquery",
      $: "jquery",
      utils: [path.resolve(__dirname, "src/js/utils"), "default"], // 変更
    }),
// ...省略
utils: {
          name: "utils",
          test: /src[\\/]js[\\/]utils/, // 変更
        },
// ...省略

resolve追加

webpack.common.js
optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0,
      cacheGroups: {
        vendors: {
          name: "vendors",
          test: /node_modules/,
          priority: -10,
        },
        utils: {
          name: "utils",
          test: /src[\\/]js[\\/]utils/,
        },
        default: false,
      },
    },
  },
  // ここから追加 resolveを設定することでpathや拡張子を省略して書くことができます。
  resolve: {
    alias: {
      "@scss": path.resolve(__dirname, "src/scss"), 
      "@imgs": path.resolve(__dirname, "src/images"),
    },
    extensions: [".js", ".scss"],
    modules: [path.resolve(__dirname, "src"), "node_modules"],
  },
  // ここまで
});

VSCodeでResolveの自動補完設定をする

root直下にjsconfig.jsonファイルを作成し以下を記入

jsconfig.json
{
  "compilerOptions": {
     "baseUrl": "./src",
     "paths": {
       "@scss": ["scss"],
       "scss": ["scss"],
       "js": ["js"],
       "images": ["images"]
     }
  }
}

VSCodeを再起動すると補完が効く

非同期でファイルを読み込む

webpack.common.js
optimization: {
    splitChunks: {
      chunks: "all", // chunksをallに設定するとdfefaultでは同期処理するようになり
      minSize: 0,    // cacheGroupsの個々のプロパティーにchunks: 'async'と設定するとで
      cacheGroups: { // 非同期で読み込むことができる。
        vendors: {
          name: "vendors",
          test: /node_modules/,
          priority: -10,
        },
        utils: {
          name: "utils",
          test: /src[\\/]/,
          chunks: 'async' // この設定を追加すると非同期で読み込まれる。
        },                // たとえば重たい画像ファイルだけを非同期にしたりすることがある

非同期で呼び出すファイルは下記のようにダイナミックインポートで指定する

app.js
import("@scss/app);

複数のHTMLファイルでスクリプトタブで生成する

以下の例ではindex.htmlにはapp.js、other.htmlにはsub.jsをスクリプトタグで生成する
src直下にother.htmlファイルを作成

other.html
<!DOCTYPE html>
<html lang="en">
  <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>this is other html</h1>
  </body>
</html>

html-webpack-pluginをインストール

yarn add --dev html-webpack-plugin
webpack.dev.js
// ...省略
    plugins: [
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        inject: "body",
        chunks: ["app"], // 追加
      }),
      // ここか追加
      new HtmlWebpackPlugin({
        template: "./src/other.html",
        filename: "other.html",
        inject: "body",
        chunks: ["sub"],
      }),
      // ここまで
    ],
  });

以上になります。
最後までお読みいただきありがとうございました。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4