23
28

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.

NextremerAdvent Calendar 2018

Day 11

webpack4の基本的な書き方を覚えて、Reactの開発環境を作る

Last updated at Posted at 2018-12-11

この記事はNextremer Advent Calendar 2018の11日目の記事です。

Reactの開発環境を構築する際 create-react-app に頼りきりで、会社のprojectのwebpack.config.jsに何が書いてあるのかわからなかったため、webpackの基本的な使い方を勉強しました。
今後、フロントエンド開発を行っていく上で、避けては通れないwebpackの超超基本的な設定方法について見ていきたいと思います。

Webpackとは?

webpackは、jsファイル画像ファイルスタイルシートなど、様々なファイルを一つにまとめる機能を持ち、その個々のファイル(モジュール)を束ねる(バンドルする)という役割から、モジュールバンドラーと呼ばれています。
webpackを使うメリットは以下の通りです。

1. ライブラリの依存関係を解消する

下記二つのファイルは、a.jsからb.jsを読み込んでいるため、.html内にscriptタグで読み込む際、b.js->a.jsの順番で読み込まなければ、存在しないファイルを参照しようとしてエラーとなります。

a.js
import name from './b.js';
console.log(name);
b.js
const name = 'dog';
export default name;
<script src="a.js"></script>
<script src="b.js"></script>
<!-- a.js内でb.jsが読み込めないためエラーとなる -->

このような依存関係を自動で解決してくれます。

2. リクエストの回数が減る

複数ファイルを一つにまとめられるので、リクエスト回数が減るのはイメージしやすいと思います。

3. 大規模な開発に向いている

最終的に一つのファイルにまとめられるので、クラスや関数など、細かい単位でのファイル分け(モジュール化)を前提とした開発が出来るため、コードの見通しがよくなるのと、名前の競合防止の役割を果たします。

ローダー

ローダーは様々な種類のものがあり、webpackを利用する上で、非常に重要な役割を果たします。
モジュールはJavascriptの概念ですが、ローダーを使用することで .css .png などのファイルもバンドルしてひとつにまとめることが出来ます。

webpack.png

今回使用するローダー

css-loader

cssファイルを読み込むためのローダー

style-loader

読み込んだcssのスタイルをstyle要素としてページに反映させるローダー

url-loader

画像ファイルをDataURL化して.jsファイルとして読み込むためのローダー

file-loader

画像をファイルとして出力し、パスを管理するためのローダー
url-loaderでJavascript化された画像データはオリジナルよりサイズが大きくなるため、一定のファイルサイズ以上は、Javascript化せずにファイルとして出力する、というような使い方をします。
url-loaderは、ファイルサイズが小さく個数の多い画像ファイルをバンドルする時に効果を発揮します。

babel-loader

モジュールをまとめる前に、babelによるトランスパイルを挟むためのローダー
*トランスパイルとは...新しいJavaScriptの記法に対応していないブラウザでは動作しないため、動作が確実な古いJavascriptの記法に変換すること。また、reactで一般的に使われるJSXという記法も、babelによって通常のJavaScript記法に変換することで、ブラウザで動作するようにします。

プラグイン

Webpackの機能を拡張するためのライブラリが多数提供されています。プラグインを利用することで、バンドルファイルから共通のモジュールを切り出したり、バンドルファイルを読み込むためのhtmlファイルを自動生成したり、バンドルされたcssを別ファイルとして出力したりといったことが出来ます。
今回使うもの下記の一つだけです。

extract-text-webpack-plugin

cssファイルをバンドルしたファイルとは別のスタイルシートとして出力するプラグイン

簡単にまとめると

  • webpackは複数のファイルをまとめるモジュールバンドラーとしての機能が主な役割
  • ファイルをまとめる時に、.js意外のものを読み込めるようにするローダー
  • その他、プラグインによって機能を拡張出来る

実践

それでは、webpackを使ってReactのコードを動かし、Hello worldを出力するまでを行っていきます。

まず、今回のプロジェクトディレクトリを作成します。

mkdir hello-webpack
cd hello-webpack/

続いて、package.jsonを作成します。package.jsonやinstallコマンドについての説明は省略します。

npm init

// 問いかけが終わるまでエンターを押します。
// すると、下記のようなpackage.jsonファイルが作成されます。

{
  "name": "hello-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

必要なnpmモジュールをinstallします。

npm install -S react react-dom
// reactに必要なモジュール
npm install -D webpack webpack-cli
// webpack本体と、webpackコマンドを使えるようにするためのもの
  • -Sオプションは--saveの省略形
  • -Dオプションは--save-devの省略形

次に、必要なローダー類をインストールします。

npm install -D css-loader style-loader url-loader file-loader
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react

babelとつくものが沢山出てきて混乱すると思うので説明していきます。
babel-loader はbabelをローダーとして使用するためのもの
@babel/core はbabel本体
@babel/preset-***とは...?
babelでトランスパイル(JS -> JSの変換)をする時に使用するもので、@babel/preset-*** のような名前で***にpresetの種類が入ります。Reactプロジェクトの場合は、@babel/preset-react により、JSXで書かれているものをJavaScriptに変換し、次に@babel/preset-envで古いJavaScriptに変換の順で行いブラウザで動作するようにします。

必要なディレクトリ、ファイルを作成します。

mkdir -p public/ src/images
touch public/index.html src/index.js src/style.css src/hello.js
touch webpack.config.js .babelrc

imagesの中にpng jpg どちらかの拡張子の画像ファイルを入れてください

hello-webpack/
├── node_modules/
├── package-lock.json
├── package.json
├── public            // バンドルしたファイルをここに吐き出す
│   └── index.html    // webpackでバンドルしたjsファイルをindex.htmlで読み込む
├── src               // バンドルする前のソースコードをここへ
│   ├── index.js      // 起点となるファイル
│   ├── Hello.js      // Helloと画像を表示するコンポーネント
│   ├── style.css
│   └── images
│        └── dog.png  // 好きな画像を入れてくだい
├── .babelrc          // babelの設定ファイル
└── webpack.config.js // webpackの設定ファイル
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>Hello Webpack</title>
</head>
<body>
  <!-- id rootの要素にReactでレンダリングする -->
  <div id="root"></div>
  <!-- webpackによりbundle.jsというファイル名でまとめたものを読み込む -->
  <script src="bundle.js"></script>
</body>
</html>
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './Hello';

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);
Hello.js
import React from 'react';
import './style.css';
import dog from './images/dog.png'; // 自分で入れた画像のファイル名、下のimgタグのsrc要素で読み込む

export default () => (
  <div>
    <div className="hello">Hello Webpack!</div>
    <img src={dog} />
  </div>
);
webpack.config.js
const path = require('path'); // パスを指定するためにnode標準で搭載されるpathを読み込む

module.exports = {
  mode: 'development',
    devtool: 'eval-source-map',
  entry: path.join(__dirname, '/src'), 
  output: {
    path: path.join(__dirname, '/public'), // バンドルしたファイルの吐き出し先を設定
    filename: 'bundle.js' // ファイルの名前を指定
  },
  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 51200, // url-loaderを使うサイズの上限を指定
              name: './images/[name].[ext]'
              // limit以上のサイズの時、どこになんて名前で画像を吐き出すのかを設定。
              // [name].[ext]は、もとの画像nameと拡張子で吐き出す。
            }
          }
        ]
      },
    ]
  }
}

webpackの設定ファイル、webpack.config.jsの内容

path

Node.jsの標準モジュール

path.join(__dirname, '/src')
// joinは引数のパスを連結させる。__dirnameはカレントのディレクトリのパスが出力される。

mode

  • production -> バンドルしたファイルが、空白/改行が除去されファイルが最小化される。
  • development ->コード判読がしやすいように、空白/改行を除去せずオリジナルのコードを維持する。

devtool

ソースマップの種類を指定。開発時には、限りなくオリジナルのソースコードを得られるeval-source-mapがおすすめ。詳しい種類については下記を参照。
https://webpack.js.org/configuration/devtool/

entry

起点となるjsファイルを指定。/srcのようにディレクトリを指定すると、そのディレクトリ配下のindex.jsがデフォルトで読まれる。

output

  • path -> 吐き出す先のディレクトリを指定。
  • filename -> 吐き出すファイルの名前を指定。

module/rules

  • test => 読み込む拡張子
  • use => 利用するローダー、配列で後ろのものから適用される。
  • exclude => 除外するディレクトリやファイル

loaderにbabelを使用しているため、babelの設定を記述する必要があります。webpack.config.jsにまとめて書くことも出来ますが、今回は.bebelrc ファイルに切り出しています。こうすることで、webpack.config.jsでbabel-loaderが使用されている時に、babelの設定は.babelrcのものが適用されます。

 .babelrc
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

"presets"には配列を渡します。envはmodulesオプションを設定するために配列になっています。
*presetは実行される際に後ろに記述したものから適用されます。上記のように記述することで、presetのreact -> envの順で解釈されます。

続いて、webpackを実行するコマンドをpackage.jsonに追加します。

package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js"
  },

--configオプションはデフォルトでwebpack.config.jsを参照。説明のために指定。

それでは、一通りの設定が終わったので、webpackを実行してみましょう

npm run build

> webpack --config webpack.config.js

Hash: f67d3e2ea3f0dc087604
Version: webpack 4.27.1
Time: 1050ms
Built at: 2018-12-11 13:23:45
    Asset      Size  Chunks             Chunk Names
bundle.js  2.11 MiB    main  [emitted]  main
Entrypoint main = bundle.js
[./node_modules/css-loader/dist/cjs.js!./src/style.css] 185 bytes {main} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {main} [built]
[./src/Hello.js] 318 bytes {main} [built]
[./src/images/dog.png] 7.9 KiB {main} [built]
[./src/index.js] 174 bytes {main} [built]
[./src/style.css] 1.06 KiB {main} [built]
    + 14 hidden modules

output先に指定したpublic/ディレクトリをみてみると

public
├── bundle.js
└── index.html

無事にbundle.jsとしてファイルが吐き出されています。
index.htmlを開いてみると
スクリーンショット 2018-12-11 12.42.25.png
無事に表示出来ていることが確認できました!

CSSファイルを別で吐き出すプラグインを設定してみる

今のままだと、cssファイルに使用しているのは [style-loader, css-loader] なので、cssファイルが読み込まれた後、style要素として挿入されています

スクリーンショット 2018-12-11 13.28.39.png

これを埋め込みではなく.cssファイルとして出力してみましょう。

必要なプラグインをインストール

npm install -D extract-text-webpack-plugin@next

下記を変更

webpack.config.js

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 追加

module.exports = {
  mode: 'development',
 devtool: 'eval-source-map',
  entry: path.join(__dirname, '/src'), 
  output: {
    path: path.join(__dirname, '/public'),
    filename: 'bundle.js'
  },
  
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },
      {
        test: /\.css$/,                   // 変更
        use: ExtractTextPlugin.extract({  // 読み込んだプラグインを適用させる。
          fallback: 'style-loader',
          use: ['css-loader'],
        }),
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 51200,
              name: './images/[name].[ext]'
            }
          }
        ]
      },
    ]
  },
  plugins: [                            // プラグインを追加
    new ExtractTextPlugin('style.css')  // 引数に出力する名前を渡す
  ]
}
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <!-- 出力されたstyle.cssを読み込む -->
  <link rel="stylesheet" href="style.css">
  <title>Hello Webpack</title>
</head>
<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>
</html>

もう一度webpackを実行してみます。

npm run build

// 無事style.cssとしてスタイルシートが出力されている
public/
├── bundle.js
├── index.html
└── style.css

以上がwebpackでReactを動かす基本的な設定となります。

開発用サーバーを立てるwebpack-dev-server

このままでは、ファイルを変更するたびにbuildコマンドを実行しなければならずとても面倒です。webpack-dev-serverを使うことで、webpackと連動しファイル変更時に自動で再ビルドされ、ブラウザー更新までしてくれます。

npm install -D webpack-dev-server
package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js",
    "start": "webpack-dev-server --open" // この行を追加
  }
  • --openはコマンド実行時に自動でブラウザで開いてくれるオプション
webpack.config.js
module.exports = {
  // ... 省略
  entry: path.join(__dirname, '/src'), 
  output: {
   // ... 省略
  },
  devServer: { // webpack-dev-serverの設定を追加
    contentBase: path.join(__dirname, '/public'), // 監視するディレクトリを指定
    port: 8084  // 立ち上げるポートを指定
  },
  module: {
    // ...省略
  }
}

設定が終わったので実際に立ち上げてみましょう。

npm start

すると、自動的にブラウザが立ち上がって、ビルドした状態のページが表示されます。なお、ビルド結果のファイルはメモリに保存されているだけなので、実際には出力されません。
webpack-dev-serverでサーバが立ち上がった状態で、src/内のファイルの変更をかけてみてください。自動で変更が検知されてブラウザに反映されたと思います。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?