Madogiwa Blog

主に技術系の学習メモに使っていきます。

Webpackを色々触ってみたので基本的な使い方とかをMEMO🕸📦

RailsにはWebpackerとか設定をいい感じにやってくれるものがありますが、 最近脱WebpackerしてWebpackをそのまま使ったり、Simpackerを導入したりするような話を耳にすることが多くなってきました。

inside.pixiv.blog

github.com

そんな中で、Webpackが何もわかってなかったので今回は下記の記事(とてもわかりやすかった🙏✨)を参考にWebpackに入門して、いろいろイジってみたのでMEMOしておきます。

ics.media

Webpackとは?

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset. https://github.com/webpack/webpack

Webpackとはモジュールバンドラーです。jsだけでなくcssやimage等もバンドルすることが出来ます。

複数に分割されたファイルを1つにまとめることでリクエスト数を削減でき、パフォーマンスの向上が見込めます。

またpluginを用いてbabel等をあわせて実行することができるまた、フロントエンドまわりのタスクを一本化できるところも魅力のようです👀

Webpackの導入方法

導入には、まず下記のコマンドを実行します。

npm i -D webpack webpack-cli

次にバンドルするファイルを作成します。Webpackはデフォルだと下記のような設定でバンドルするので、それぞれ作成します。

項目 内容
entry src/index.js
output dist/main.js

※entry: バンドルする対象のファイル、output: バンドル後の出力先

あとはpackage.jsonのscriptsにビルド用のコマンドを追加してあげて、

  "scripts": {
    "build": "webpack",

npm run buildで実行出来ます📦✨

$ npm run build
Hash: 9ca4e2bf9b0dda113944
Version: webpack 4.42.1
Time: 190ms
Built at: 2020-04-19 12:23:29
  Asset       Size  Chunks             Chunk Names
main.js  952 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/index.js 131 bytes {0} [built]

Webpackをカスタマイズする

Webpackをカスタマイズするには、webpack.config.jsを作成してそこに値を記載していきます。

例えばbuildモードをdevelopmentに変更する場合は下記のようにします。

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development"
}

他にも色々な項目が用意されているので詳しくはこちら👇

webpack.js.org

サンプル集

ちょっと個人的に色々設定をいじってみたので、サンプル集として載せておきます!

webpack-dev-serverを使う

毎回ファイルを編集したときにnpm run buildを実行するのは面倒くさいですし、全量ビルドが走るので効率がよくないです。そういう場合にはwebpack-dev-serverが便利です。(webpack --watchでも良いですが)

github.com

npm i -D webpack-dev-serverを実行してwebpack-dev-serverをinstall

下記のようにwebpack-dev-server用の設定をwebpack.config.jsに追加

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  }

scriptsにも起動用のコマンドを追加して、

  "scripts": {
    "build": "webpack",
    "build-dev-server": "webpack-dev-server",

npm run build-dev-serverを実行すると、Webpackビルド用のサーバーが立ち上げって自動的に差分ビルドが走るようになります🤖

ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/morita.jun/Documents/repo/javascript/webpack_study/dist
ℹ 「wdm」: wait until bundle finished: /
ℹ 「wdm」: Hash: 560347cd3ca9f479cd19
Version: webpack 4.42.1
Time: 1816ms
Built at: 2020-04-19 12:33:53

ダイジェストを付与してバンドルする

webpackでバンドルしたときに毎回同じファイル名で出力してしまうとブラウザキャッシュにより、 うまく変更が反映されないといった問題が発生します。

なので出力時のファイル名にhashを付与するように設定を変更してみます。

やり方は簡単でoutputfilenameの中で[hash]をつけてあげるだけです。

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // 出力設定
  // デフォルトはdist/main.js
  output: {
    // 出力先のディレクトリ
    path: `${__dirname}/dist`,
    // 出力先のファイル
    filename: 'main-[hash].js'
  },
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  }
}

この設定で実行するとmain-b562cb2621248a80cacb.jsのようにdigestを付与することが出来ます。

しかし、digestを付与してあげるとhtmlでスクリプトを読み込むときに毎回srcを変更しないといけないのが不便です。

そんなときは、html-webpack-pluginが便利です。

github.com

npm install -D html-webpack-pluginで導入して下記のように設定すると自動的にsrcを指定してくれます。

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // 出力設定
  // デフォルトはdist/main.js
  output: {
    // 出力先のディレクトリ
    path: `${__dirname}/dist`,
    // 出力先のファイル
    filename: 'main-[hash].js'
  },
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  },
  plugins: [
    // distのHTMLを自動生成するplugin、digest等をよしなに対応してくれる
    new HtmlWebpackPlugin(),
  ]
}

下記のように良い感じにsrcを設定してくれます👍

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Webpack App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"></head>
  <body>
  <script src="main-c6653b46c86daeb1df0d.js"></script></body>
</html>

entryを複数用意してentry毎にバンドルする

デフォルトの設定だとentryもoutputも一つのファイルなので、 実際のプロジェクトだとentry/outputが肥大化して、初回のリクエストに時間が掛かる等が問題になることもあるかと思います。

webpackのentryとoutputの設定を変更することで、entryとなるファイルと出力先を複数ファイルにして、 ファイルの肥大化を抑えることが出来ます。

下記の設定では、entryをsrc/index.jssrc/home.jsとして、そのentryファイル毎に出力するようにしています。※[name]を使うことでentryのファイル名を取得できる。

またHtmlWebpackPluginのオブジェクトをentry単位に用意してそれぞれを読み込むようにしています📦

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // jsのエントリーポイント
  // デフォルトはsrc/index.js
  entry: {
    main: './src/index.js',
    home: './src/home.js'
  },
  // 出力設定
  // デフォルトはdist/main.js
  output: {
    // 出力先のディレクトリ
    path: `${__dirname}/dist`,
    // 出力先のファイル
    filename: '[name]-[hash].js'
  },
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  },
  plugins: [
    // 個別のbuild後のjsを読み込むHTMLを生成
    new HtmlWebpackPlugin({
      filename: 'main.html',
      chunks: ['main']
    }),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      chunks: ['home']
    })
  ]
}

cssをバンドルする

RailsだとcssのビルドはSproketsで行うことが多いですが、Webpackでもcssのバンドルを行うことが出来ます。

Webpackでcssのビルドを行うためにはstyle-loadercss-loaderが必要です。

github.com

github.com

下記でinstallします。

npm -D style-loader css-loader

設定は下記のような形です。moduleruleに適用したいloader(今回だとstyle-loadercss-loader)の設定を追加します。

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // jsのエントリーポイント
  // デフォルトはsrc/index.js
  entry: {
    main: './src/index.js',
    home: './src/home.js'
  },
  // 出力設定
  // デフォルトはdist/main.js
  output: {
    // 出力先のディレクトリ
    path: `${__dirname}/dist`,
    // 出力先のファイル
    filename: '[name]-[hash].js'
  },
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  },
  module: {
    rules: [
      // stylesheetのbuild用の設定
      {
        // 対象のファイル
        test: /\.css/,
        // 使用するloader
        use: [
          "style-loader",
          "css-loader"
        ]
      }
    ]
  },
  plugins: [
    // 個別のbuild後のjsを読み込むHTMLを生成
    new HtmlWebpackPlugin({
      filename: 'main.html',
      chunks: ['main']
    }),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      chunks: ['home']
    })
  ]
}

これでcssのbundleが行われるようになりました🙌 ※実際は下記のようにjs内でimportして使うので、jsファイルとしてバンドルされ動的にhtmlに反映されます。

// src/index.js
import "./styles/index.css"

バンドル時にbabelを実行する

リアルワールドではIE11といったレガシィブラウザを扱うことも多いので、babelを通す必要があるケースが多いです。

Webpackにはbabel用のプラグインも用意されていて比較的簡単にbabelを実行してビルドすることが出来ます。

github.com

まずは必要なものをinstallします。

npm install -D babel-loader @babel/core @babel/preset-env
  • babel-loader : Webpack用のloader
  • @babel/core : babelの本体
  • @babel/preset-env : Babelのプラグインを自動判定してよしなにしてくれるやつ

その後は下記のようにbabel用の設定を追加します。

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ビルド時のモード
  // development: ビルド時間が短くソースマップに対応しているが容量の圧縮がかからない。
  // production: 本番用build、コメント削除等の圧縮がかかり容量が少ない(デフォルト)
  mode: "development",
  // jsのエントリーポイント
  // デフォルトはsrc/index.js
  entry: {
    main: './src/index.js',
    home: './src/home.js'
  },
  // 出力設定
  // デフォルトはdist/main.js
  output: {
    // 出力先のディレクトリ
    path: `${__dirname}/dist`,
    // 出力先のファイル
    filename: '[name]-[hash].js'
  },
  // webpack-dev-server用の設定
  devServer: {
    // webpackのoutput pathを指定
    contentBase: `${__dirname}/dist`,
    // 実行時にブラウザを開く
    open: true
  },
  module: {
    rules: [
      // stylesheetのbuild用の設定
      {
        // 対象のファイル
        test: /\.css/,
        // 使用するloader
        use: [
          "style-loader",
          "css-loader"
        ]
      },
      // babelのbuild用の設定
      {
        // 対象のファイル
        test: /\.m?js$/,
        // 対象外のファイル
        exclude: /(node_modules|bower_components)/,
        // 使用するloader
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  },
  plugins: [
    // 個別のbuild後のjsを読み込むHTMLを生成
    new HtmlWebpackPlugin({
      filename: 'main.html',
      chunks: ['main']
    }),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      chunks: ['home']
    })
  ]
}

これでbabelも合わせて実行されるようになりました🗼

おわりに

今回はwebpackを色々触ってみました。今まで触ったことがなかったので何もわからなかったのですが、単純なことをやるだけであれば既存のライブラリが充実しているので意外と設定出来そうな感じがしました(便利✨)

参考

qiita.com

ics.media