Webpack による環境構築

Webpack 環境構築 プログラミング学習

  1. yarn のインストール
  2. package.json の初期化
  3. Webpack をインストールする
  4. パッケージを実行する方法
    1. グローバルパッケージ
    2. ローカルパッケージ
  5. Webpack を使ってバンドルする
  6. webpack.config.js
    1. webpack.config.js とは
    2. webpack.config.js の書き方
      1. module.exports = { … }
      2. mode オプション
      3. entry オプション
        1. 複数のエントリーポイント、出力は1つ
        2. 複数のエントリーポイント、出力も複数
      4. output オプション
        1. output オプションの基本
        2. 出力ファイル名で [name] を使用する
  7. Loader
    1. Loader とは
    2. Sass ファイルを CSS に変換する
      1. ライブラリをインストールする
      2. webpack.config.js の module オプション
      3. エントリーポイントで Sass ファイルを import する
      4. webpack コマンドを実行する
    3. PostCss で CSS にベンダープレフィックスを自動で付与させる
      1. PostCSS とは
      2. ライブラリをインストールする
      3. postcss.config.js を作成
      4. webpack.config.js の CSS 処理のルールを変更
      5. webpack コマンドを実行する
    4. CSS による画像の読み込み(File-loader)
      1. CSS で画像を読み込むとバンドルに失敗する
      2. file-loader
        1. file-loader をインストールする
        2. webpack.config.js に画像ファイル用のルールを追記
        3. webpack コマンドを実行する
  8. 出力ファイル名にハッシュ値を付与する
    1. ブラウザのキャッシュ機能
    2. キャッシュの問題点
    3. ハッシュ値
    4. ハッシュ値を使ってみる
  9. Babel
    1. Babel をインストールする
    2. webpack.config.js(注意:完全ではない)
    3. Babel の設定ファイル(.babelrc)
    4. Babel によるトランスパイルを試す
    5. Babel の設定ファイル2(babel.config.js)
      1. babel.config.js
      2. targets で対象ブラウザを指定
      3. targets に browserslist のクエリを使用する
    6. core-js@3 と regenerator-runtime
      1. インストール
      2. ソースコードで import する
      3. babel.config.js に設定を追加する
  10. ESLint
    1. ESLint のインストール
    2. ESLint の設定ファイル(.eslintrc)
    3. webpack.config.js にルールを追記
    4. ESLint の設定ファイル(.eslintrc)の詳細
      1. “env”
      2. “globals”
      3. “parserOptions”
      4. “rules”
    5. fix オプションでコードを自動的に修正する
    6. 自動で設定ファイルを生成する
  11. mini-css-extract-plugin で CSS を分離する
    1. mini-css-extract-plugin をインストールする
    2. webpack.config.js に設定を追加

yarn のインストール

npm install -g yarn

package.json の初期化

プロジェクトを管理する設定ファイル package.json を生成するコマンド

npm の場合リンク

npm init -y
// または npm init --yes

yarn の場合

yarn init -y
// または yarn init --yes

Webpack をインストールする

npm の場合リンク

npm install webpack webpack-cli -D
// npm install webpack webpack-cli --save-dev

yarn の場合

yarn add webpack webpack-cli --dev

パッケージを実行する方法

グローバルパッケージ

グローバルにインストールしたパッケージであれば、path が通っているディレクトリにインストールされるため、単純にそのパッケージのコマンドを打ち込めばよい。

コマンド

ローカルパッケージ

ローカルにインストールしたパッケージのコマンドは、node_modules/.bin/ 配下に格納されている。したがって、.bin/ ディレクトリに path が通っていないと「コマンドが見つからない(CommandNotFoundException)」というエラーが発生する。

これを避けるためには次のようにする。

npm の場合

npx コマンド

yarn の場合

yarn run コマンド

Webpack を使ってバンドルする

yarn run webpack

このコマンドを実行すると、js ファイルがバンドルされ、新たに「dist」ディレクトリが作成され、その中にバンドルした結果である「main.js」が生成される。

ただし、次のようなワーニングが発生する。

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

これは「mode」オプションが設定されていないことを警告している。mode オプションは、商用環境(production)/開発環境(development)のどちらのファイルを生成するかを指定するもの。

商用環境(production)

yarn run webpack --mode production

開発環境(development)

yarn run webpack --mode development

webpack.config.js

webpack.config.js とは

webpack コマンドを実行するときのオプション指定は、webpack.config.js という設定ファイルにまとめることができる。

webpack コマンドを実行するときに webpack.config.js が存在する場合は、その設定内容が読み込まれ、自動的に解析されてバンドルに反映される。

ちなみに、この設定ファイルは –config オプションでファイル名を指定することで、デフォルトの webpack.config.js から変更することができる。例えば以下のようにすると、webpack.dev.js が設定ファイルとなる。

yarn run webpack --mode development --config .\webpack.dev.js

webpack.config.js の書き方

module.exports = { … }

webpack コマンドに対するオプションを、オブジェクトリテラルで記述し、node.js のモジュール構文である module.exports を使用して外部に公開し、ファイル外からでも設定内容を使用できるようにする。例えば、先ほどの

yarn run webpack --mode development

というコマンドは、webpack.config.js を

module.exports = {
  mode: 'development'
}

のように作成すると、

yarn run webpack

mode オプション

先ほども説明したように、mode オプションは商用環境(production)開発環境(development)のどちらのファイルを生成するかを指定するもの。

module.exports = {
  mode: 'development'
}

entry オプション

entry オプションにはバンドルのエントリーポイントとなるファイルを指定する。つまり、entry オプションに指定したファイルを起点として、webpack は import 命令によるファイルの依存関係の解析を開始し、バンドルを実行する。

この entry オプションを省略した場合は、プロジェクトの src ディレクトリ配下にある index.js がエントリーポイントとして使用される。

例えば、次のように指定すると、バンドルのエントリーポイントは src ディレクトリの app.js となる。

module.exports = {
  mode: 'development',
  entry: './src/app.js'
}
複数のエントリーポイント、出力は1つ

また、エントリーポイントとなるファイルを複数指定し、1つのファイルとしてバンドルすることもできる。その場合はそれらのファイルは互いに依存関係を持たないものとする。

以下のように配列で指定する。

module.exports = {
  mode: 'development',
  entry: ['./src/app.js', './src/app2.js']
}
複数のエントリーポイント、出力も複数

エントリーポイントとなるファイルを複数指定し、出力ファイルも複数に分けるということもできる。

以下のようにオブジェクトリテラルで指定する。

module.exports = {
  mode: 'development',
  entry: {
    bundle1: './src/app.js',
    bundle2: './src/app2.js'
  }
}

このようにすると、エントリーポイント ./src/app.js を起点としたバンドルの結果は ./dist/bundle1.js、エントリーポイント ./src/app2.js を起点としたバンドルの結果は ./dist/bundle2.js として出力される。

output オプション

output オプションの基本

output オプションを使用することで、バンドルの出力先のファイル名とディレクトリを指定することができる。省略した場合は dist ディレクトリの main.js ファイルが出力先となる。

path プロパティに出力先のディレクトリを絶対パスで指定する。filename プロパティには出力先のファイル名を指定する。

絶対パスを指定するためには下のサンプルのように、パスを操作するための path という Node.js に備わっているモジュールを読み込み、これを使用して絶対パスを取得する。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  }
}

このように webpack.config.js を作成し webpack コマンドを実行すると、public ディレクトリが作成され、その中にバンドルの結果として bundle.js が生成される。

出力ファイル名で [name] を使用する

filename プロパティに出力ファイル名を指定するときに [name] という変数を使用することができる。この [name] の部分には、エントリーポイント(entry オプション)に指定したオブジェクトリテラルのプロパティ(キー)が格納される。

例えば次のようにすると、出力ファイル名は a.bundle.js となる。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {a: './src/app.js'},
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: '[name].bundle.js'
  }
}

複数のエントリーポイントを持つ場合は、それぞれのキーを出力ファイル名に使用したファイルに分けられる。

例えば次のようにすると、a.bundle.js と b.bundle.js という2つの出力ファイルが生成される。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    a: './src/app.js',
    b: './src/app2.js'
  },
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: '[name].bundle.js'
  }
}

Loader

Loader とは

webpack は複数の JavaScript ファイルの依存関係を解決して一つにまとめる(バンドルする)機能をもっていることを説明した。

webpack に追加の機能を設けると、バンドルの際に特別な処理を実行させることや、JavaScript 以外のファイルを webpack を使ってバンドルすることもできる。

バンドル対象のファイル読み込んで webpack で処理できる形式に変換するためのライブラリをローダー(Loader)と呼んでいる。

Sass ファイルを CSS に変換する

webpack に Loader をインストールして、Sass ファイルを CSS ファイルに変換してみる。

ライブラリをインストールする

以下のライブラリをインストールする。

  • sass : Sass をコンパイルして CSS に変換する。
  • sass-loader : webpack を使用して Sass をコンパイルするためのもの。
  • css-loader : CSS を JavaScript に変換する。
  • style-loader : JavaScript の中にある CSS 文字列を HTML に <style> タグで注入する。
yarn add --dev sass sass-loader css-loader style-loader

webpack.config.js の module オプション

webpack.config.js の module オプションに、バンドル対象のファイルをローダー(Loader)で処理するためのルール(rules)を記述する。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        // test に対象となるファイルの拡張子を指定
        test: /\.scss$/,
        // use にどのローダーを適用するかを指定
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  }
}

use の配列に記述したローダーは、後ろのものから順番に実行されていくことに注意が必要。つまり一番最初に実行させたいローダーが一番最後に記述される。

エントリーポイントで Sass ファイルを import する

実際に webpack で Sass ファイルを CSS に変換してバンドルするためには、エントリーポイントとなるファイル(上記の webpack.config.js では ./src/app.js)で Sass ファイルを import する必要がある。

./src/app.scss

import './app.scss';

webpack コマンドを実行する

以下のようなサンプルで webpack によるバンドルを実行してみる。

├─ node_modules/
├─ public/
│    └─ index.html
└─ src/
      ├─ app.js
      └─ app.scss

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>Webpack Sample</h1>
  <script src="bundle.js"></script>
</body>
</html>

src/app.js(エントリーポイント)

import './app.scss';

src/app.scss

$title_color: red;

h1 {
  color: $title_color;
}

コマンドラインに次のように webpack コマンドを打ち込んで、webpack によるバインドを実行させる。

yarn run webpack

このコマンドの実行により生成される public/bundle.js をエディタで開き、「h1」で検索してみると、Sass ファイル src/app.scss に記述したスタイル定義の情報が文字列として組み込まれていることが分かる。

Sass ファイル src/app.scss に記述したスタイル定義の情報が、public/bundle.js に文字列として組み込まれている

この画像に「__CSS_LOADER_EXPORT__」とあるように、sass-loader で Sass から CSS に変換したものを、css-loader によってバンドル結果の JavaScript ファイルに文字列として組み込んでいる。

ただし、これだけでは bundle.js を読み込んだ index.html にスタイルを適用することはできない。index.html に bundle.js から <stytle> タグを注入できるように、さらなる変換を行なうのが style-loader の役目になっている。

style-loader による変換後、最終的に得られたバンドル結果 bundle.js を読み込んだ index.html のブラウザへの出力は次のようになる。

bundle.js を読み込み実行することにより、index.html の <head> タグ内に <style> タグが注入された

index.html のソースコードには、<head> タグ内に <style> タグの記述は存在しないが、ブラウザが index.html の <script> で指定された bundle.js を読み込み実行することにより、index.html の <head> タグ内に <style> タグが注入され、その結果スタイルが適用されることになる。

PostCss で CSS にベンダープレフィックスを自動で付与させる

PostCSS とは

PostCSS は、CSS に何か特別な処理を加えるときに使用する CSS フレームワークであり、autoprefixer というプラグイン(plugin)を合わせて使用することで、CSS にベンダープレフィックスを自動的に付与することができる。

ライブラリをインストールする

以下のライブラリをインストールする。

  • postcss-loader : webpack で PostCSS を使用するためのもの。
  • autoprefixer : PostCss を使用して CSS にベンダープレフィックスを自動で付与させるためのプラグイン

npm の場合

npm install --save-dev postcss-loader autoprefixer

yarn の場合

yarn add postcss-loader autoprefixer --dev

postcss.config.js を作成

PostCss を使用するためには、専用の設定ファイル postcss.config.js が必要になる。

postcss.config.js に次のように記述することで、プラグインとして autoprefixer を使用して PostCSS を動作させる、という設定を行なうことができる。

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

webpack.config.js の CSS 処理のルールを変更

webpack.config.js の module オプションの rules に記載した Sass の処理に、PostCSS による CSS の処理を加えるため、postcss-loader を追加する。

sass-loader による Sass から CSS への変換と css-loader による CSS から JavaScript への変換の間に PostCSS の処理を追加したいので、次のように use の配列を変更する。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        // test に対象となるファイルの拡張子を指定
        test: /\.scss$/,
        // use にどのローダーを適用するかを指定
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader', // <-- 追加
          'sass-loader'
        ]
      }
    ]
  }
}

webpack コマンドを実行する

これでベンダープレフィックスを自動で付与させるための準備は終了したので、実際に CSS にベンダープレフィックスが自動で付与されることを確認してみる。

確認するためには、ベンダープレフィックスが付与されるプロパティを使用する必要がある。2021年2月の現時点では、例えば「text-size-adjust」プロパティはベンダープレフィックスが必要なので、これを使ってみた。

app.scss

$title_color: red;

h1 {
  color: $title_color;
  text-size-adjust: none;
}

このように Sass ファイルを変更し、webpack コマンドを実行してバンドルしてみる。

ブラウザで index.html を表示させてみると、ベンダープレフィックスが付与されたスタイルが注入されていることを確認できる。

ベンダープレフィックスが付与されたスタイルが注入されていることを確認

CSS による画像の読み込み(File-loader)

CSS で画像を読み込むとバンドルに失敗する

CSS で背景画像の読み込みなどのため URL を指定している場合、webpack でバンドルしようとするとエラーが発生する。

例えば次のようにCSS(Sass)で背景画像を url() を使用して指定してみる。

├─ node_modules/
├─ public/
│    └─ index.html
└─ src/
      ├─ app.js
      ├─ app.scss
      └─ images/
          └─ bg.gif

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>Webpack Sample</h1>
  <div class="logo"></div>
  <script src="bundle.js"></script>
</body>
</html>

src/app.js

import './app.scss';

src/app.scss

$title_color: red;

h1 {
  color: $title_color;
}

.logo {
  background-image: url('./images/bg.gif');
  width: 100px;
  height: 100px;
}

webpack コマンドを実行すると次のようなエラーが発生する。

ERROR in ./src/images/bg.gif 1:6
Module parse failed: Unexpected character '' (1:6)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

CSS を JavaScript に変換する際に、app.scss で URL を指定して読み込んでいる画像 bg.gif の解析に失敗しているようだ。「You may need an appropriate loader to handle this file type,(このファイルタイプを処理するには、適切なローダーが必要になる場合があります。)」と言われている。

このように CSS に画像のような形式のファイルを読み込む記述を含む場合、webpack で CSS をバンドルするにあたって、これらのファイルを適切に処理するためのローダーが必要になる。

主に次の2通りの処理が考えられる。

  • 画像のような形式のファイルも JavaScript で扱える形式に変換してしまう。
  • ファイルは変換せずに、パス(URL)の情報を適切に処理する。

前者を行なうためには、url-loader というローダーが使用される。後者を行なうためには、file-loader というローダーが使用される。

以下では後者の file-loader を使用したやり方を説明する。

file-loader

file-loader をインストールする

npm の場合

npm install file-loader --save-dev

yarn の場合

yarn add file-loader --dev
webpack.config.js に画像ファイル用のルールを追記

webpack.config.js の module オプションの rules に、バンドル時に画像などのフィル形式を見つけた場合の処理のルールを追記する。

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(jpe?g|gif|png|svg|woff2?|ttf|eot)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'images',
              publicPath: 'images'
            }
          }
        ]
      }
    ]
  }
}

woff(woff2)、ttf、eot の拡張子は Web フォントのためのものであり、CSS から Web フォントを読み込む場合も画像ファイルと同様に、バンドル時の処理を指定する必要があるため一緒に記述した。

options に指定した各プロパティの意味は次のようになっている。

  • name プロパティ
    対象のファイルを出力用のディレクトリに配置するときのファイル名
    [name] は元のファイル名、[ext] は元の拡張子を埋め込む。
  • outputPath プロパティ
    対象のファイルを出力用のディレクトリに配置するときのディレクトリ
    ‘images’ と指定した場合、出力用のディレクトリに imges ディレクトリが作成される。
  • publicPath プロパティ
    画像を別のサーバーに配置する場合などに使用する。ここで指定したパスがブラウザからファイルを読み込むときに使用されるようになる。同じサーバーに配置するのなら、outputPath と同じにすればよい。
webpack コマンドを実行する

webpack.config.js をこのように変更した後で webpack コマンドを実行すると、出力用のディレクトリ(public)の配下に images ディレクトリが作成され、その中にファイルが配置(コピー)される。

├─ node_modules/
├─ public/
│    ├─ index.html
│    └─ images/
│         └─ bg.gif
└─ src/
     ├─ app.js
     ├─ app.scss
     └─ images/
          └─ bg.gif

また、ブラウザで HTML を開くと app.scss で読み込んだ画像ファイルのパスが適切に処理され、ファイルが適切に読み込まれていることを確認することができる。

file-loader によって、CSS で読み込んだ画像ファイルのパスが適切に処理されている

出力ファイル名にハッシュ値を付与する

ブラウザのキャッシュ機能

ある Web サイトにブラウザで初めて訪問した場合、ブラウザはそのサイトのデータを持っていないため、サイトの表示に必要なすべてのファイルをWebサーバーから取得することになる。

そして取得したファイルはブラウザのメモリ領域などに保存(キャッシュ)しておき、次回の訪問時には、変更されていないファイルについてはブラウザでキャッシュしているものを使用することで、Web サーバーにリクエストするファイルの数を減らし、その結果サイトの表示を高速化している。

キャッシュの問題点

ある Web サイトのデータをブラウザでキャッシュしている状態で、サーバー側で何かしらの問題箇所の修正などを行ない、ファイルが変更された場合について考えてみよう。

この場合、ブラウザがその Web サイトを再度訪問した場合には、ブラウザ側でキャッシュしている(変更前の)ファイルを使ってしまうと、変更が反映されていないページが表示されることになる。

この問題を避けるためには、変更されたファイルについては、ブラウザ側でキャッシュしているファイルを使用するのではなく、必ず Web サーバーにリクエストしてファイルを取得するようにしたい。

それを実現するための方法として、ファイル名にバージョンのような一意の値を付与し、ファイルの内容が変更されたらその値、つまりファイル名が変更されるようにする、というようなことが行われる。ファイル名が異なればブラウザはキャッシュしているファイルを使用せずに、リクエストによりサーバーからファイルを取得しようと試みるためだ。

そして当然だが、ファイル名を変更したら、そのファイルをリクエストするときの URL も同様に変更されるようにする必要がある。

ハッシュ値

webpack を用いてこれを行なう場合には、ファイル名にハッシュ値と呼ばれるランダムな文字列を付与する方法が取られる。

webpack.config.js における output オプションの filename プロパティに、バンドル後に生成されるファイルの名前を設定する。この filename で使用できるテンプレート(template)の中には次のようなハッシュ値を生成するものが存在する。(Template strings

  • [fullhash]
    生成されるすべてのファイルで同じハッシュ値が付与される。
  • [chunkhash]
    エントリーポイントが複数ある場合に、エントリーポイント毎に(chunk 毎に)ハッシュ値が付与される。
  • [contenthash]
    生成されるファイル毎に異なるハッシュ値が付与される。

画像ファイルでは [contenthash] がよく使用され、JavaScript ファイルや CSS ファイルでは [chunkhash] がよく使用される。

一方で [fullhash] に関しては、ファイルの変更があると全てのファイルのハッシュ値が変更されるため、変更後のブラウザによる初回訪問時は全てのファイルをリクエストすることになり、処理が重くなる。それを問題と思うなら [contenthash] か [chunkhash] を使用する方がいい。

ハッシュ値を使ってみる

webpack.config.js

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.[fullhash].js'
  }
}

これで webpack コマンドを実行すると、public ディレクトリには

bundle.a44c9545e362ac534b8d.js

のようなハッシュ値が付与されたファイルが生成されるようになる。

また、src/app.js の内容を変更して再度 webpack コマンドを実行すると、

bundle.5d1d11809771ba1a94e8.js

のように異なるハッシュ値のファイルが生成されることを確認できる。

Babel

Babel を使用することで、ES6 以降で書かれた JavaScript のコードを、ES5 相当のコードに変換(トランスパイル)することができる。以下では webpack と Babel を連携させる方法について説明する。

Babel をインストールする

npm の場合

npm install -D babel-loader @babel/core @babel/preset-env

yarn の場合

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

webpack.config.js(注意:完全ではない)

webpack と Babel を連携させるために、module の rules に babel-loader の記述を追加する。(後述するが、以下の記述は不完全であり、後ほど target オプションを追加する)

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

node_modules は、babel-loader によるトランスパイルの対象外のディレクトリとなるように、exclude で指定している。node_modules にはインストールしたライブラリファイルが格納されているが、これらは既にトランスパイルされているため、babel-loader によるトランスパイルが不要なため。

Babel の設定ファイル(.babelrc)

Babel 本体の設定ファイルとして .babelrc という名前のファイルを作成し、その中に JSON 形式で次のように記述する。

{
  "presets": ["@babel/preset-env"]
}

以上で基本的な Babel の設定がとりあえず完了した。

Babel によるトランスパイルを試す

以下のサンプルで Babel の動作を確認してみた。

src/app.js

const func = () => {
  console.log('babel test');
}
func();

webpack コマンドを実行し、トランスパイルしてみると、生成された bundle.js の中身は次のようになっていた。(出力される bundle.js を読みやすくするために、webpack.config.js で devtool: false を指定していることに注意)

public/bundle.js

/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!********************!*\
  !*** ./src/app.js ***!
  \********************/
var func = function func() {
  console.log('babel test');
};

func();
/******/ })()
;

確かに src/app.js で定義したアロー関数は ES5 の記述に変換されていることが分かる。しかし、コード全体を囲っている即時関数(// webpackBootstrap の部分)はアロー関数で表現されている、、、おかしい。ググると同じことを質問している人がいる(stackoverflow)。

webpack.config.js に次のように target オプションを指定する必要があるようだ。このオプションに ‘web’ と ‘es5’ を指定することで、ブラウザ環境向け、かつ ES5 の JavaScript コードにトランスパイルしてくれる。(リンク

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  target: ['web', 'es5'], // <-- 追加
}

再度 webpack コマンドを実行し、トランスパイルしてみると、bundle.js の中身は次のようになった。

/******/ (function() { // webpackBootstrap
var __webpack_exports__ = {};
/*!********************!*\
  !*** ./src/app.js ***!
  \********************/
var func = function func() {
  console.log('babel test');
};

func();
/******/ })()
;

Babel の設定ファイル2(babel.config.js)

babel.config.js

上では Babel 本体の設定を .babelrc ファイルに記述したが、他にも babel.config.js という JavaScript ファイルを作成し、そこに設定を記述する方法をとることもできる。

babel.config.js は JavaScript ファイルなので、その中で関数を使用することができるというメリットが存在する。そのため複雑な Babel の設定を記述することができる。

babel.config.js

module.exports = api => {
  api.cache(true);

  return {
    "presets": ["@babel/preset-env"]
  }
}

このように記述すると先述した .babelrc と同じ設定を行なったことになる。

targets で対象ブラウザを指定

次のように targets オプションを指定すると、

  • ie11(Internet Explorer 11)で実行できる。
  • chrome 60 で実行できる。

という制限を設けてトランスパイルを行なわせることができる。

module.exports = api => {
  api.cache(true);

  return {
    "presets": [
      ["@babel/preset-env", {
        // 対象となるブラウザを指定
        targets: {
          ie: "11",     // Internet Explorer 11
          chrome: "60"  // chrome 60
        }
      }]
    ]
  }
}

targets に browserslist のクエリを使用する

targets オプションに、browserslist のクエリ(query)を指定することで、対象ブラウザの条件を記述することもできる。

module.exports = api => {
  api.cache(true);

  return {
    "presets": [
      ["@babel/preset-env", {
        targets: [
          "last 2 versions",
          "> 0.5%",
          "maintained node versions",
          "not dead"
        ]
      }]
    ]
  }
}

core-js@3 と regenerator-runtime

インストール

Babel の公式サイトにも書かれているように、Babel はアロー関数のような「構文の変換」を行なうが、対象ブラウザに不足している機能は @babel/polyfill というモジュールによって補間されるため、これをインストールする必要がある。

しかし、@babel/polyfill の公式サイトを見てみると、このパッケージ(モジュール)は現在は非推奨になっており、代わりに「core-js/stable」と「regenerator-runtime/runtime」を直接 import するように指示されている。

したがってこれらをインストールする。

npm の場合

npm install --save core-js/stable regenerator-runtime

yarn の場合

yarn add core-js/stable regenerator-runtime

この2つのモジュールは次のような機能をもっている。

  • core-js/stable
    Promise, Symblo, Map など、古いブラウザで実装されていない機能を補完する。
  • regenerator-runtime
    ジェネレータ関数や async 関数を実行できないブラウザの機能を補完する。

ソースコードで import する

webpack.config.js の entry オプションで指定したエントリーポイントとなるファイルに次の import 文を追加する。(リンク

import "core-js/stable";
import "regenerator-runtime/runtime";

ただし、このように import するとすべての機能がインポートされてしまうため、ビルド後のファイルサイズが大きくなってしまう。

babel.config.js に設定を追加する

必要な機能だけをインポートさせたい。そのためには babel.config.js に次のような設定を追加する。

babel.config.js

module.exports = api => {
  api.cache(true);

  return {
    "presets": [
      ["@babel/preset-env", {
        targets: [
          "last 2 versions",
          "> 0.5%",
          "maintained node versions",
          "not dead"
        ],
        useBuiltIns: "usage", // <-- 追加
        corejs: 3 // <-- 追加
      }]
    ]
  }
}

「useBuiltIns: ‘usage’」を指定すると、ソースコードを解析して、コードの中で使用されている機能だけをインポートしてくれるようになる。また、「useBuiltIns: ‘usage’」を指定する場合は、core-js のバージョンも指定する必要がある。

この設定を追加すると、エントリーポイントに追加した import 文は不要になる。(パッケージのインストールは必要)

// 以下の import は不要
// import "core-js/stable";
// import "regenerator-runtime/runtime";

ESLint

JavaScript の構文チェックツールである ESLint と webpack を連携させる方法について説明する。

ESLint のインストール

以下のモジュールをインストールする。

  • eslint : ESLint 本体
  • eslint-loader : ESLint を webpack で使用できるようにするためのもの。
  • babel-eslint : ESlint と Babel を連携させるためのもの。

npm の場合

npm install eslint eslint-loader babel-eslint --save-dev

yarn の場合

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

ESLint の設定ファイル(.eslintrc)

ESlint の設定を .eslintrc ファイルに JSON 形式で記述する。

{
  "env": {
    "browser": true,
    "es2017": true
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint"
}

webpack.config.js にルールを追記

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          'babel-loader',
          'eslint-loader'
        ]
      }
    ]
  },
  target: ['web', 'es5'],
}

‘eslint-loader’ で構文チェックを行なってから ‘babel-loader’ でトランスパイルを行なうという順番になるように use の配列を記述している。(後のものから実行される)

ちなみにこれは次のように書いてもよい。

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader'
      }
    ]
  },
  target: ['web', 'es5'],
}

あるいは enforce: ‘pre’ を記述すれば、同じ拡張子に対して必ず先に実行されるめ、次のように書くこともできる。

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader'
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
    ]
  },
  target: ['web', 'es5'],
}

ESLint の設定ファイル(.eslintrc)の詳細

“env”

”env” には ESLint がチェックする環境の設定を行う。下の記述ではブラウザ環境で実行され、ES2017 の文法に従う JavaScript としてチェックするように設定している。(リンク

{
  "env": {
    "browser": true,
    "es2017": true
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint"
}

“globals”

グローバル変数を ESLint に明示的に教えるためには、”globals” オプションを使用する。例えば下の例では、globalVar は変更可能(”writable”)であり、globalObj は変更不可(”readonly”)であることを設定している。(リンク

“env” で設定した値に対応したグローバル変数(グローバルオブジェクト)なら ESLint は正しくチェックできるが、ユーザーが独自に使用しているグローバル変数などは “globals” オプションに登録する必要がある。

{
  "env": {
    "browser": true,
    "es2017": true
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint",
  "globals": {
    "globalVar": "writable",
    "globalObj": "readonly"
  }
}

“parserOptions”

“parserOptions” オプションではサーポートする JavaScript のオプション設定をすることができる。

下の例では、”sourceType”: “module” により、ソースコードを ECMAScript のモジュールで管理していることを示している。デフォルトは “script”。(リンク

{
  "env": {
    "browser": true,
    "es2017": true
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint",
  "parserOptions": {
    "sourceType": "module"
  }
}

“rules”

“rules” オプションでカスタマイズしたチェックを設定できる。(リンク

“extends”: “eslint:recommended” でおススメのルールの適用を設定した上で、この “rules” オプションでカスタマイズするという使い方をする。

{
  "env": {
    "browser": true,
    "es2017": true
  },
  "extends": "eslint:recommended",
  "parser": "babel-eslint",
  "parserOptions": {
    "sourceType": "module"
  },
  "rules": {
    "no-undef": "off",
    "semi": ["error", "always", {"omitLastInOneLineBlock": true}]
  }
}

fix オプションでコードを自動的に修正する

webpack.config.js において、eslint-loader に適用する rules に次のような options を追加する。このようにするとコードが自動的に修正されるようになる。

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {  // <-- 追加
          fix: true // <-- 追加
        }           // <-- 追加
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
    ]
  },
  target: ['web', 'es5'],
}

自動で設定ファイルを生成する

次のコマンドを実行する。

yarn run eslint --init

mini-css-extract-plugin で CSS を分離する

mini-css-extract-plugin を使用すると、本来 webpack によって JavaScript ファイルにバンドルされるはずの CSS ファイルを分離することができる。

CSS ファイルを分離することで、CSS に変更がないときにブラウザのキャッシュ機能により、Webサイトを再訪問した時の表示の高速化を期待することができる。

mini-css-extract-plugin をインストールする

npm の場合

npm install --save-dev mini-css-extract-plugin

yarn の場合

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

webpack.config.js に設定を追加

インストールした mini-css-extract-plugin が働くようにするには、webpack.config.js に次のような記述を追加する。

  • require() を使用して mini-css-extract-plugin を読み込む。
  • plugins オプションの配列に mini-css-extract-plugin から生成したインスタンスを追加する。
  • module オプションの rules で、CSS(Sass) の処理に mini-css-extract-plugin の ローダーを追加する。
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  },
  target: ['web', 'es5'],
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'style.css'
    })
  ]
}

これで、webpack コマンドを実行してビルドを行なえば、出力ファイルのディレクトリに指定した名前の CSS ファイルが生成される。

タイトルとURLをコピーしました