webpack


使用したバージョン

```
"webpack": "^3.11.0"
```

# webpackとは

Webクライアントアプリを作るためのNode.js製ビルドツール。

# 概要

![webpack.png](https://qiita-image-store.s3.amazonaws.com/0/70065/728786cf-5d33-35df-0639-9ac945bbebf7.png "webpack.png")

webpacはエントリーファイルを持ち、最終的にJavaScriptファイルを出力する。

この際、エントリーファイルに **import** されたモジュールを指定された **loader** を使って解決していく。
また用意されたPluginを組み合わせることで、関連するファイル(html,css等）を自動生成させることが出来る。

これらのエントリーファイルや出力先、使用するloader、Plugin等は **webpack.config.js** ファイルに定義する。

```webpack.config.js
module.exports = {
	entry: 'エントリーファイル.js',
	output: {
		path: '出力先ディレクトリ',
		filename: '出力ファイル名.js',
	},
	module; {
		rules: [
		    /*
		     使用するloaderを登録する。
		     wepbackは、指定されたloaderを使って、importされたファイルをJavaScriptに変換する。
		     */
		     
			{
			   //例: jsxファイルがimportされたらbable-loaderを使って解決する
				test: /\.jsx$/, 
				loader: 'babel-loader',
				options: {
					presets: ["react"]
				}
			}
		]
	},
	pulugins: [
	// index.htmlファイルを生成するプラグインや、リソースをコピーするプラグインなど様々なプラグインが用意されている。
	]
}
```

# インストール

```console:terminal
$ npm install --save-dev webpack
```

# 使い方

1. webpack.config.js を作成する
2. webpack コマンドでビルドする

```console:terminal
$ webpac
```

* オプション
	* `-w` `--watch` 変更を監視
	* `-d` 開発用ビルド
	* `-p` リリース用ビルド

# loader

**import** されたモジュールをJavaScriptに変換する役割を担う。

webpackは、configファイルで定義されたloaderに対し、 **下から順に** 問い合わせ、出力をチェーンしていく。

loaderの定義

| | 説明 |
|:---------|:--------|
|test     |変換対象モジュールを指定する。(正規表現)|
|loader   |使用するloader。後ろの `-loader` は省略可能。|
|options  |loaderが使用するoption。loaderに直接クエリ形式で指定することも出来る。|

---

Ex1 jsxファイルがimportされた場合は、babel-loaderを使って解決する

```webpack.config.js

{
	test: /\.jsx/,
	loader: 'balel' //-loaderは省略可
	options: {
		presets: ['react']
	}
}
```

Ex2 pngなどのリソースがimportされた場合は、file-loaderを使って解決する
(file-loaderを使うと、importの出力値として、ファイルパスに変換してくれる。)

```webpack.config.js
{
	test: /\.(png|jpg|gif)$/,
	loader: 'file?name=[name].[ext]&context=./app/static/' // optionsは、クエリ形式で指定できる
}
```


## 用意されているloader

webpackで用意されているloaderはこちらで確認できる。
https://webpack.js.org/loaders/


## [babel-loader](https://webpack.js.org/loaders/babel-loader/)

babel-loaderは、JavaScriptファイル(ES6 or JSX)を旧来のJavaScript(ES5)に変換する。

### インストール

```console:terminal
$ npm i --save-dev babel-core babel-loader
$ npm i --save-dev babel-preset-es2015 babel-preset-react
```

### 使い方

```webpac.config.js
// babel-loader 
{
    loader: 'babel-loader' 
    test: /.js$/,
    options: {
        presets: ['es2015', 'react'] 
    }
}
```

```app.js
// jsファイルは拡張子が不要
import Foo from 'foo'
```

### サンプル

以下のサンプルは、ES6対応のJavaScript(app.js)をwebpackでビルドして、旧来のJavaScript(ES5)を生成する。

```console:ファイル構成
* sample
	* app.js
	* foo.js
	* package.json
	* webpack.config.js
```

インストール

```terminal
$ npm init -y
$ npm i --save-dev webpack 
$ npm i --save-dev babel-core babel-loader
$ npm i --save-dev babel-preset-es2015
```

ファイルの作成

```webpac.config.js
const path = require('path')
module.exports = {
	entry: './app.js',
	output: {
		filename: './out/app.js'
	},
	module: {
		rules:[
			{
				test: /\.js$/,
				loader: 'babel-loader',
				options: {
					presets: ['es2015']
				}
			}
		]
	}
}
```

```foo.js
export default class Foo {
	constructor () {
		this.message = "Hello World"
	}
}
```

```app.js
import Foo from 'foo'

const foo = new Foo()
console.log(foo.message)
```

ビルド

```terminal
$ ./node_module/.bin/webpac
```

またpackage.jsonに"build"を定義すると `npm run build` でビルド出来る。

```package.json
scripts: {
	"build": "webpack"
}
```

生成されたout/app.jsの内部を見ると、foo.jsが、ES5に変換されてapp.jsファイルにexportされている。

```out/app.js
// 省略
var Foo = function Foo() {
    _classCallCheck(this, Foo);

    this.message = "Hello World";
};

exports.default = Foo;
```


## [file-loader](https://webpack.js.org/loaders/file-loader/)

file-loaderは、webpackにfileとしてオブジェクトを出力するように命令する。
またimport結果としてファイルパスを渡す。

Ex: icon.pngをimportした際の、file-loaderによる出力結果

```App.js
// file-loaderによって読み込む
import img from '../public/imgs/icon.png'
console.log(img.constructor.name) // String
console.log(img)  // 出力パスは、webpack.config.jsで制御する
```

* imgにはicon.pngへのファイルパスが格納される
* icon.pngは出力先にコピーされる

### インストール

```
$ npm --save-dev file-loader
```

### 使い方

```webpack.config.js
{
	test:   /\.(png|jpg|gif)$/,
	loader: 'file-loader'
}
```

#### オプション

|キー| 説明 |
|:---|:----|
|name             |**出力するファイル名**|
|context          |作業ディレクトリを指定する。デフォルトはwebpack.config.contextと同一。nameの[path]に影響を与える。|
|publicPath       |独自にpublicPathを指定する場合に使用する|
|outputPath:      |独自にoutputPath(コピー先)を指定する場合に使用する。nameの[path]に追加される。|
|useRelativePath  |出力に、contextを考慮するか。デフォルトでは `true`|
|emitFile         |importされたファイルを出力先にコピーするかどうかを指定する|


##### placeholder

nameには以下のplaceholderが用意されている。

|値   | 説明|
|:----|:-------------|
|name |ファイル名(パスや拡張子は含まれない)|
|ext  |ファイル拡張子|
|path |ディレクトリパス(context, outputPathの影響を受ける)|
|hash |コンテントのハッシュ値|
|N    |regExpでマッチングした値を後方参照出来る|

Ex regExpによる後方参照によるnameの指定方法

```app.js
import img from './customer01/file.png'
```

```webpack.config.js
{
  loader: 'file-loader',
  options: {
    regExp: /\/([a-z0-9]+)\/[a-z0-9]+\.png$/, //グルーピングでディレクトリ名を取得
    name: '[1]-[name].[ext]' 
  }  
}
```

出力結果

```
customer01-file.png
```

このように **file-loader** はimportの出力値を webpack.config.js 側で定義する。

### サンプル

サンプルは、外部のcssを読み込み、linkタグを動的に追加するmain.jsを作成します。

ファイル構成

以下のようにdevフォルダ内でソースを管理し、ビルド結果をdist配下に出力するようにします。

```console:ファイル構成
app -+- package.json
     |
     +- webpac.config.s
     |
     +- dev -+- main.js
     |       |
     |       +- css - sample.css
     |       |
     |       +- img - sample.png
     |
     +- dist -+- index.html(静的に作成しておく)
```

webpack.config.js

```webpack.config.js
const path = require('path')
module.exports = {
    entry: './dev/main.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js',
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                loader: 'file-loader',
                options: {
                    name: '[path][name].[ext]',
                    context: './dev',
                    outputPath: 'assets',
                }
            },
        ]
    }
}
```

MEMO:

* context = 作業ディレクトリを `./dev` 配下にすることで、[path]の出力から除外する。
* outputPath = assetsディレクトリ配下にリソースをコピーするようにする。また[path]にも追加される。

これによってfile-loaderによるimportの出力結果は以下となる。

```
assets/[images|css]/[name].[ext]
====================
   = [path]
```

main.js

```dev/main.js
import styleFileName from './css/sample.css'
import './img/example.png' 

document.addEventListener('DOMContentLoaded', (event) => {
    const aLink = document.createElement('link')
    aLink.type = 'text/css'
    aLink.rel = 'stylesheet'
    aLink.href = styleFileName

    const head = document.getElementsByTagName('head')[0]
    head.appendChild(aLink)
})

```

example.css

```dev/css/example.css
.content {
        background-image: url(../images/sample.png);
}

h1 {
    color: red
}
```

４. index.html

```dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body class="content">
    <h1>Hello World</h1>
    <!- ビルドしたmain.jsを組み込む ->
    <script type="text/javascript" src="./main.js"></script>
</body>
</html>
```

５. ビルド & 表示結果

![Screen Shot 2018-02-22 at 14.02.43.png](https://qiita-image-store.s3.amazonaws.com/0/70065/b88bffa7-c48d-43dd-1cf3-ad1be5361ea5.png "Screen Shot 2018-02-22 at 14.02.43.png")


## [css-loader](https://webpack.js.org/loaders/css-loader/)

css-loaderは、cssファイルを解析し、JavaScriptオブジェクトに変換する。

その際に、 **＠import** と **url()** を **require** 、 **import** と同じように扱い、それらの解決も行う。


```sample.css
.sample {
	color: red;
	// url()をハンドリングすると モジュールのimportと同一に扱う。
	backgruond-image: url('./images/example.jpg');
}
```

```main.js
// css-loaderによって読み込む
import sample from 'sample.css'

console.log(sample)
/* 出力結果
[ [ 27,
    '.sample {\n    color: red;\n    background-image: url(images/example.jpg);\n}\n',
    '' ],
  toString: [Function: toString],
  i: [Function] ]
*/
  

console.log(sample.toString())
/* 出力結果
sample {
	color: red;
	background-image: url(images/example.jpg);
}
*/
```

### インストール

```
$ npm install --save-dev css-loader
```


### 使い方

webpack.config.js

```webpack.config.js
{
	test: /\.css$/,
	loader: 'css-loader'
}
```

app.js

```app.js
import style from 'sample.css' //JavaScriptオブジェクトに変換される
```

##### ビルド後のurl()の入力値がコピー先と異なる可能性がある

css-loaderは、url() を import と同じように扱うため、webpackに登録されている他のloaderに対して解決を問い合わる。
その際にfile-loaderを使用している場合、file-loaderは **import** の出力値を **webpack.config.js** 側で定義するため、ビルドして生成されたcss側で設定されているurl()の値がコピー先と異なる可能性がある。

以下例

```console:ファイル構成
dev-+-css-sample.css
    |
    +-images-sample.png
```

```sample.css
p { background-image: url('.../images/sample.png') } 
```

上記において、例えばwebpack.config.js側が以下だとする。

```webpack.config.js
// file-loader
{
	test: /\.png$/,
	loader: 'file?name=images/[name].png'
}
```

ビルド後の出力結果は以下となり、コピー先と異なってしまっている事がわかる。

```build/assets/css/sample.css
p { background-image: url('images/sample.png') }
```

解決策としては、context、outputhPath、nameの[path]を上手く使う必要があります。
(サンプルを参照)

### サンプル

サンプルは、cssファイルを読み込み、動的にHTMLにインジェクトするapp.jsを作成する。

ファイル構成

dev配下でソースを管理し、ビルド結果はdist配下に出力する。

```console:ファイル構成

app -+- package.json
     |
     +- webpac.config.s
     |
     +- dev -+- main.js
     |       |
     |       +- css - sample.css
     |       |
     |       +- images - sample.png
     |
     +- dist -+- index.html(静的に作成しておく)
              |
              +- main.js
              |
              assets -+- images - example.png
```


ファイルの作成

webpack.config.js

```webpack.config.js
const path = require('path')
module.exports = {
    entry: './dev/main.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            //css-loader
            {
                test: /\.css$/,
                loader: 'css-loader',
            },
            //file-loader
            {
                test: /\.png$/,
                loader: 'file-loader',
                options: {
                    name: '[path][name].[ext]',
                    context: './dev',
                    outputPath: 'assets',
                }
            },
        ]
    }
}
```

sample.css

```dev/css/sample.css
h1 {
    color: red;
}

.bg {
	background-image: url(../images/example.png);
}
```


main.js

```dev/main.js
import sample from './css/sample.css'

document.addEventListener('DOMContentLoaded', (event) => {
    const style = document.createElement('style')
    style.type = 'text/css'
    
    // 読み込んだcssを書き込む
    style.innerHTML = sample.toString()
    const head = document.getElementsByTagName('head')[0];
    head.appendChild(style)
})
```

index.html 

```index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body class="bg">
    <h1> Hello Wrold </h1>
    <!-- 生成したJavaScriptを読み込む -->
    <script src="./main.js"></script>
</body>
</html>
```

ビルド

```console:terminal
$ webpack
```

表示結果

![Screen Shot 2018-02-24 at 14.09.19.png](https://qiita-image-store.s3.amazonaws.com/0/70065/70152913-ddf9-1bc3-f27f-c34b63d0b59a.png "Screen Shot 2018-02-24 at 14.09.19.png")


## [style-loader](https://webpack.js.org/loaders/style-loader/)

cssをDOMに追加する。

style-loader単体では、importを直接解決せず、file-loaderやcss-loaderと組み合わせて使用する。

### 使い方1 styleタグをDOMにインジェクトする

style-loaderは、**css-loader** と組み合わせることで、importされたcssをstyleタグに変換し、動的にHTML内に組み込ませるJavaScriptを生成する。

従って先にcss-loaderによってimportが解決される必要がある。
(webpackは下側から順にloaderに問い合わせる）

```webpack.config.js
{
	test: /\.css$/,
	use: [
		{ loader: 'style-loader'},
		{ loader: 'css-loader' }
	]
}
```

```sample.css
p {
	color: red;
}
```

```app.js
import './sample.css'
```

ビルドして生成したapp.jsファイルをindex.htmlに組み込めば完了

```index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <p> Hello World</p>
    <script src="./out/main.js"></script>
</body>
</html>
```

表示結果
app.jsによって、styleタグが動的に埋め込まれる。

|表示結果|inspector|
|:----:|:--------:|
|![Screen Shot 2018-02-21 at 13.21.30.png](https://qiita-image-store.s3.amazonaws.com/0/70065/5d1dff07-c224-5c82-672b-5794f4bbcfe1.png "Screen Shot 2018-02-21 at 13.21.30.png")|![Screen Shot 2018-02-21 at 13.22.08.png](https://qiita-image-store.s3.amazonaws.com/0/70065/587e4ff9-d47e-e3c0-b007-823d1150b145.png "Screen Shot 2018-02-21 at 13.22.08.png")|


### 使い方２ linkタグをDOMにインジェクトする

file-loader と組み合わせることによって、linkタグとしてcssファイルを動的に組み込むJavaScriptに変換される。

またwebpac.donfig.jsでは、先にfile-loaderを実行するようにする。
(webpackは下側から順に問い合わせる）


```webpack.config.js
{
	test: /\.css$/,
	use: [
		{ loader: 'style-loader/url'}, //url
		{ loader: 'file-loader' },
	]
}
```

```sample.css
p {
	color: red;
}
```

```app.js
import './sample.css'
```

上記をビルドして、index.htmlファイルに組み込む。

```index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <p> Hello World</p>
    <script src="./out/app.js"></script>
</body>
</html>
```


表示結果
app.jsによってlinkタグが動的に埋め込まれる

|表示結果|inspector|
|:----:|:--------:|
|![Screen Shot 2018-02-21 at 13.21.30.png](https://qiita-image-store.s3.amazonaws.com/0/70065/5d1dff07-c224-5c82-672b-5794f4bbcfe1.png "Screen Shot 2018-02-21 at 13.21.30.png")|![Screen Shot 2018-02-21 at 14.05.29.png](https://qiita-image-store.s3.amazonaws.com/0/70065/b7022087-c741-b996-ec77-c273be50f34c.png "Screen Shot 2018-02-21 at 14.05.29.png")|


# プラグイン

webpackにおいてプラグインは、関連するファイルを自動生成するために使用される。

## HtmlWebpackPlugin

HTMLを自動生成する。
また生成したJavaScriptファイルをbodyにインジェクトする。

### インストール

```
$ npm install --save-dev html-webpack-plugin
```
  
### 使い方1 htmlファイルの自動生成

１. webpack.config.jsの用意

```webpac.config.js
module.exports = {
    entry: './app.js',
    output: {
        path: path.join(__dirname, 'out'),
        filename: '[name].js'
    },
    plugins: [
        new HtmlWebpackPlugin(),
    ]
}

```

２. entryファイルの用意

```app.js
document.addEventListener('DOMContentLoaded', (event) => {
    const body = document.getElementsByTagName('body')[0]
    body.innerHTML = 'Hello World'
})
```

３. ビルドする

```
$ webpack
```

４. 出力結果

index.htmlが自動生成され、 **app.js** ファイルが組み込まれる。

```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
  <script type="text/javascript" src="app.js"></script></body>
</html>
```

### 使い方２ テンプレートを使用する

webpack.config.js

```webpac.config.js
plugins: [
    new HtmlWebpackPlugin({
        template: './template.html',
        filename: 'index.html',
    })
]
```

template.html

```template.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1 id="root"></h1>
</body>
</html>
```

template.htmlを元に、index.htmlファイルが生成され、JavaScriptファイルも組み込まれる。

```out/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1 id="root"></h1>
<script type="text/javascript" src="main.js"></script></body>
</html>
```

## ExtractTextWebpackPlugin

cssファイルを自動生成する。

(※ style-loaderは、importされたcssを動的にhtmlにインジェクトするJavaScriptに変換する。一方このプラグインを使用すれば、静的なcssファイルとして出力してくれる。）

css-loader, style-loaderと組み合わせて使用する。

### インストール

```
$ npm install --save-dev extract-text-webpack-plugin
$ npm install --save-dev css-loader style-loader
```

### 使い方

```webpack.config.js
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    // importされたcssをstyles.cssファイルに出力する
    new ExtractTextPlugin("styles.css"),
  ]
}
```

### サンプル

サンプルとして、DOMのbadyに書き込むapp.jsを作成し、htmlとcssをそれぞれプラグインを使って自動生成する。

ファイル構成

```
app -+- webpack.config.js
     |
     +- package.js
     |
     +- app.js
     |
     +- sample1.css
     |
     +- sample2.css
```

webpack.config.js

```webpack.config.js
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')


module.exports = {
  entry: './app.js',
  output: {
      path: path.join(__dirname, 'out'),
      filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
    new HtmlWebpackPlugin(),
  ]
}
```

３. app.js

```app.js
import './sample1.css'
import './sample2.css'

document.write('<h1>Hello World</h1>')
document.write('<p>Have a nice day!</p>')
'''

４. samples.css

```sample1.css
h1 {
	color: red;
}
```

```sample2.css
p {
	color: blue;
}
```

５. ビルド

```
$ webpack
```

６. 出力結果

outディレクトリに以下が生成されます。

* index.html
* style.css
* main.js

```oud/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  <link href="styles.css" rel="stylesheet"></head>
  <body>
  <script type="text/javascript" src="main.js"></script></body>
</html>
```

> 動的に生成したjsファイルとcssファイルが組み込まれている

index.html表示結果

![Screen Shot 2018-02-24 at 14.29.51.png](https://qiita-image-store.s3.amazonaws.com/0/70065/71dc3176-d4ee-440b-635e-e911ef1635f1.png "Screen Shot 2018-02-24 at 14.29.51.png")

※ style-loaderによって、app.jsが動的にstyleタグを組み込んでいないか確認するために、ためにし、生成したindex.htmlのlinkタグを削除して表示してみて下さい。

## CopyWebpackPlugin

リソースファイルのコピー

file-loaderはimportされたファイルのみ出力先にコピーされる。
このプラグインを使えば、該当ディレクトリ配下にあるリソースをビルドはいかにコピーすることが出来る。

### 使い方

```webpack.config.js
plugins: [
    new CopyWebpackPlugin([{
        from: './imgs',    //コピー元
        to: 'assets/imgs'  //コピー先
    }])
]
```

# まとめ

使用するloaderによってimport結果が異なるため、定義する順番も含めて、どういったloaderが使われているのかは注意しておく必要があります。

# 公式リファレンス

https://webpack.js.org/

# 参考書籍

[いまどきのJSプログラマーのための Node.jsとReactアプリケーション開発テクニック](https://www.amazon.co.jp/%E3%81%84%E3%81%BE%E3%81%A9%E3%81%8D%E3%81%AEJS%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE-Node-js%E3%81%A8React%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-%E3%82%AF%E3%82%B8%E3%83%A9%E9%A3%9B%E8%A1%8C%E6%9C%BA/dp/4802611145/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=K4T2C5XV528XS6KVFP01)
  
webpackだけでなく、npm、babel等の使い方が丁寧に解説されています。

# 参考サイト
## webpackについて

[[Qiita] step by stepで始めるwebpack](https://qiita.com/howdy39/items/48d85c430f90a21075cd)

[[Qiita] Webpackってどんなもの？](https://qiita.com/kmszk/items/45fb4690ace32216ca25)

[[Qiita] file-loaderで画像を扱うときのパス指定](https://qiita.com/tomi_shinwatec/items/ef66a60950939618c449)

[[Medium] Webpack Loaders, CSS and Style Loaders](https://medium.com/a-beginners-guide-for-webpack-2/webpack-loaders-css-and-sass-2cc0079b5b3a)

[[stack overflow] Webpack style-loader vs css-loader
](https://stackoverflow.com/questions/34039826/webpack-style-loader-vs-css-loader)

[[Quora] How do you run react js code on your local?](https://www.quora.com/How-do-you-run-react-js-code-on-your-local)

[Setting up CSS Modules with React and Webpack](https://javascriptplayground.com/css-modules-webpack-react/)

## npmについて
[[Qiita] npm](https://qiita.com/ysn551/items/1d2f4a804b4caf641474)