7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Webpack4とBabel(ES6)を使う

Last updated at Posted at 2018-11-10

Webpack4とBabelを使うに当たってのテンプレートです。

簡単な説明

Webpackとは

node.jsで動くモジュールバンドラーです。コーディング時はモジュール単位で開発して、ビルド時にCSS、画像を含むすべてのコードを1つのJSにまとめて吐き出すことができます。
今回は、CSS、画像を別々にするパターンも書いてます。

Babelとは

最新のJavaScriptの機能をブラウザのサポートを待たずにして使えるようにするためのトランスパイラです。
たとえばES6で書いたコードをどのブラウザでも動く旧来のコードに変換します。
Webpackと連携して使います。

初期化とインストール

node.jsなのでnpm initします。
設定に関してはお好みですがエンター連打でいいと思います。

npm init

Webpack関連をインストールします。
開発時に、保存でオートリロードをしたいのでwebpack-dev-serverも入れておきます。

npm install --save-dev webpack webpack-cli webpack-dev-server

webpack.config.jsで使用するプラグイン群をインストールします。
説明は後述します。

npm install --save-dev webpack-merge html-webpack-plugin clean-webpack-plugin

CSSと画像をビルドに含めるためにloader系のモジュールをインストールします。

npm install --save-dev style-loader css-loader url-loader

ES6(ES2015)を使用するためにBabel関連をインストールします。バージョンは7系です。

npm install --save-dev @babel/core @babel/preset-env babel-loader

Webpack Configの用意

コマンドのオプションでも指定できますが、通常はconfigファイルを作成して各種設定を行います。
configの作り方はいろいろあるかと思いますが、基本的に公式ドキュメントに従って開発用と本番用のファイルを作ります。

仕組みとしては共通的な設定ファイルとしてwebpack.common.jsを作成し、開発用と本番用の差分の設定をwebpack.dev.jswebpack.prod.jsに記述する形になります。これらのファイルはwebpack-mergeによって自動的にマージされます。

webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
    app: './dev/js/index.js',
  },
  plugins: [
    new CleanWebpackPlugin(['build']),
    new HtmlWebpackPlugin({
      title: "Sample HTML"
    }),
  ],
  output: {
    filename: './js/[name].bundle.js',
    path: path.resolve(__dirname, 'build')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    },
    {
      test: /\.css/,
      use: ['style-loader', {
        loader: 'css-loader',
        options: {
          minimize: true,
          sourceMap: process.env.NODE_ENV === 'development',
        },
      }, ],
    },
    {
      test: /\.(gif|png|jpg)$/,
      use: [{
        loader: 'url-loader'
      }]
    }]
  }
};
  • plugins

    • clean-webpack-pluginは指定したディレクトリの中身をすべて削除するプラグインです。
    • html-webpack-pluginは指定したhtmlファイルに<script>タグを挿入してビルドしたJSを自動的にバインドするプラグインです。templateにhtmlファイルを設定しない場合はindex.htmlが勝手に作成されます。独自のhtmlを使う場合は、template:'dev/index.html'という項目を追加します。
  • module

    • babel-loaderの部分はBabelのトランスパイラに関する設定です。
    • style-loadercss-loaderはCSSファイルをJSにバンドルのに必要です。
    • url-loaderは画像ファイルをBase64に変換してJSにバンドルします。※あまり大きいサイズの画像には向かない。
    • sourceMapはソースとビルド結果の紐付けを行い、ブラウザのコンソールでエラー箇所が分かるようになります。今回はdeproymentの時のみ有効にしてます。
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist'
  }
});
  • devServerwebpack-dev-serverの監視対象ディレクトリ。portのデフォルトは8080です。
webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production',
});
  • modeproductionになっているとコンパイル時にコードが圧縮されます。
    • 自分で圧縮内容を設定する場合は別途UglifyJS2のプラグインが必要。

package.json

npm-scriptsを記述して、開発サーバの立ち上げと本番用のビルドが簡単に行えるようにします。
ここで上記の開発と本番の設定ファイルを指定します。

package.json
  "scripts": {
    "start": "NODE_ENV=development webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

各ファイルの用意

サンプルなので何でもいいですが、以下のようなファイルを用意しました。
jQueryも使ってみます。

npm install jquery --save
index.js
import $ from "jquery"
import '../styles/index.css'
import pic from '../images/example.png'

$(() => {
    let div = $("<div>").attr({id: "sample"}).text("Sample Text");
    $("body").append(div);

    let img = new Image();
    img.src = pic;
    $("body").append(img);
});
styles/index.css
#sample {
    font-size: 3.0rem;
}

images/example.pngも適当なのを用意する。

最終的なディレクトリ構成図

.
├── dev
│   ├── images
│   │   └── example.png
│   ├── js
│   │   └── index.js
│   └── styles
│       └── index.css
├── node_modules
├── package-lock.json
├── package.json
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

実行してみる

開発モード

npm startを実行するとhttp://localhost:8080でブラウザが立ち上がります。
image.png
index.jsを開いて"Test Text"などに書き換えるとブラウザの表示が自動的に書き換わるはずです。

本番用ビルド

npm run buildを実行するとdistディレクトリ以下にバンドル済みのファイル群が作成されます。
index.htmlをブラウザで開くと上記と同じように"Sample Text"が表示されるはずです。

CSSと画像を外出しする

JSにマージせずファイルとして独立させたい場合は、以下のモジュールの追加が必要です。
またstyle-loaderurl-loaderが不要になるので消しときます。

npm install --save-dev mini-css-extract-plugin file-loader
npm uninstall --save-dev url-loader style-loader

webpack.common.jsの以下3点を変更します。

  • JSからCSSを除外して別ファイルとして分離するためにMiniCssExtractPluginを追加。
    • style-loaderは不要なので削除する。
  • 画像をファイルとして扱うためにfile-loader`を追加。
    • url-loaderは不要なので削除する。
  • css-loaderoptions.url:falseに変更。url()を変換しなくなります。falseにないとBase64でアクセスしようとするのでエラーになる。

修正後の全体は以下です。

webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  entry: {
    app: './dev/js/index.js',
  },
  plugins: [
    new CleanWebpackPlugin(['build']),
    new HtmlWebpackPlugin({
      title: "Sample HTML"
    }),
    // MiniCssExtractPluginを追加、cssの出力先を指定
    new MiniCssExtractPlugin({
      filename: './styles/[name].css',
    })
  ],
  output: {
    filename: './js/[name].bundle.js',
    path: path.resolve(__dirname, 'build')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    },
    {
      test: /\.css/,
      use: [
        MiniCssExtractPlugin.loader, // <--追加:CSSをJSから分離する
        //'style-loader', <--削除
        {
          loader: 'css-loader',
          options: {
            url: false, // <--追加:url()を変換しない
            minimize: true,
            sourceMap: process.env.NODE_ENV === 'development',
          },
        },
      ],
    },
    // url-loaderを削除してfile-loaderを追加
    {
      test: /\.(gif|png|jpg)$/,
      loader: 'file-loader',
      options: {
        name: './images/[name].[ext]'
      }
    }]
  }
};

これでnpm run buildするとbuild以下が分離されるようになります。

.
├── build
│   ├── images
│   │   └── example.png
│   ├── index.html
│   ├── js
│   │   └── app.bundle.js
│   └── styles
│       └── app.css

その他

直接は関係ないのですが、GitHubとかに上げる時、npmが作成する/node_modulesやビルド結果の/buildが混じっていると面倒なので除外しておくのがいいと思います。

.gitignore
/node_modules
/build

おわりに

  • できるだけ簡単な構成にしたかったけど依存モジュールが多くて結構ややこしいことになった。流行り廃りが早いから構成の管理が大変そう。
  • SPAだったらindex.htmlだけでいいけど、複数ページに渡る場合はエントリーポイントを分けたりしないといけない。ここらへんが参考になりそう。
    • ただその場合、React使うだろうし、Reactを使うならcreate-react-appでプロジェクト作成すれば上記の設定はほとんど自動で作成されるのでそちらのほうが良い。
    • サーバーサイドでhtml生成するならそもそもWebpackは使わない??

参考文献

https://numb86-tech.hatenablog.com/entry/2018/10/24/221130
https://ics.media/entry/17376#procon-css
https://qiita.com/tsuuuuu_san/items/582854a4043d8a1db1c9
http://www.kantenna.com/pg/2018/05/webpack_scss.php

7
7
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
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?