今年のうちにwebpackについてハンズオンも交えて理解する

# webpackとは何か
JavaScript の モジュールバンドラです。
モジュールバンドラとは、
複数のファイルを１つにまとめて出力してくれるツールのことです。
（複数ファイルをまとめることを「バンドル」と呼びます。）

## なぜ使うのか

* たくさんのjsファイルを１ページで読み込もうとすると、リクエストがその数だけ発生し、時間がかかってしまう→１つのファイルにまとめることで、リクエストも１回だけできる

* 外部モジュールを利用するときにリクエスト数を減らせる

* モジュールに切り分けた開発可能→開発効率アップ

* 依存関係を解決してくれる

```
<!-- app.jsはjQueryに依存しているため、jQueryを読み込んだ後に読み込む必要がある。 -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="js/app.js"></script>
```
上記のような依存関係が増えれば増えるほど依存関係がわからなくなってしまいます。

→webpackを利用すれば依存関係を解決してファイルをバンドルするため、スクリプトの順番を考慮する必要はなくなります！

## webpackの設定ファイルの書き方

```webpack.config.js```という名前のファイルを作成し設定を書きます

webpack設定の基本は**mode**, **entry**, **output**, **loader**, **plugin**の5つです。

``` javascript:webpack.config.js
const path = require('path');

module.exports = {
  mode: "production",
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
  plugins: [
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
  }),],
};
```


### Mode

none, development, productionのいずれかを設定できるパラメータです。値別に設定を切り分けたい時に設定します。デフォルトではproductionが設定されています。

### Entry

処理の開始となるポイントを記述します。

### Output

生成されたバンドルされたファイルの出力先を記述します。

### Loader

webpackコアだけではjsファイルしか扱えません。そこでJavaScript以外のファイルでもwebpackでバンドルできるようにするものです。

例：css-loader(CSSファイルを読み込む) file-loader(画像を読み込む)

### Plugins

バンドルの仕方に作用するものです。バンドル自体の作成方法も変更できます。プラグインはローダーよりも細かい設定ができます。
例：MiniCssExtractPlugin(CSSを別バンドルで出力する)

# webpack ハンズオン


##  1. webpackの導入

### 1.webpackのインストール

まずはwebpackをnpmでインストールします。

``` 
npm init -y 
```

``` 
npm install webpack
```

``` 
npm install webpack-cli
```

## 2. bundleできているか確認しよう

ここからは二つのjsファイルを作成し、片方からもう片方をimportしている時、webpackでbundleするとどうなるのか実践していきます。

### 1. src/index.jsの作成
まずはsrcディレクトリを作成してそこに```index.js```ファイルを作成します。
このファイルでは配下のnameファイルを読み込んでconsole.logで出力するという簡単なものです。


```javascript:src/index.js
const name = require("./name");

console.log("Hello " + name);
```

### 2. src/name.jsの作成

こちらでは```name.js```で任意の名前をexportsするファイルを作成します。

```javascript:src/name.js
module.exports = "John";
```

### 3. webpack.config.jsの作成
ここで今回の主役となるwebpackの設定ファイルである
```webpack.config.js```を次のように定義します。


```javascript:webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
};
```

この設定はdecelopモードで```./src/index.js```を起点としてjsファイルをまとめて
```dist```配下の```bundle.js```というファイルに出力してください。
ということを意味しています。

###  4. webpack build

設定ファイルができたので実際にwebpackを下記コマンドで動作してみましょう

```
npx webpack build  
```

```dist```配下に```bundle.js```というファイルができたのを確認できたでしょうか？


###  5. 動作確認

実際にnodeで```bundle.js```を動作させてみると
Hello Johnが出力されて2ファイルを1ファイルにまとめることができたことがわかると思います。

```
node ./dist/bundle.js

// Hello John
```


## 3. loaderを作成してみよう

ここからはtxtファイル作成した後```index.js```で呼び出し

txtファイルはloaderなしではbundleされないことを確認した後

loaderを作成してtxtファイルをbundleできるようにしたいと思います。

### 1.src/hello.txtの作成
```hello.txt
こんにちは
```

### 2. src/index.jsで呼び出す

``` javascript
// src/index.js
const name = require("./name");

const hello = require("./hello.txt");
console.log("Hello" + name + hello);
```

### 3. webpack build

```
npx webpack build  
```

### 4.動作確認

```
node ./dist/bundle.js
// ReferenceError: こんにちは is not defined
```

エラーが出てうまくバンドルできていないことが
確認できると思います。
webpackではjsファイルしかbundleすることができず
js以外のファイルをbundleするためにはloaderが必要になります。

### 5. loaders/textLoader.jsの作成
textファイルをwebpackでbundleできるようにするために
```loaders```ディレクトリを作りその配下に```textLoader.js```
を作成します。
```javascript:loaders/textLoader.js
module.exports = function (contents) {
    return `module.exports = ${JSON.stringify(contents)};`; // Node.js で扱えるようにテキストファイルを変換
}
```

### 6. webpack.config.jsの修正
```webpack.config.js```を修正して```.txt```で終わるファイルには先ほど作成した
```textLoader.js```を適応するようにします。

```javascript:webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  // 以下を追加
  module: {
    rules: [
      {
        // 正規表現で適応したいファイル名を書く
        test: /\.txt$/,
        // 正規表現に合致したファイルに対して適応するloaderを書く
        use: [{ loader: path.resolve(__dirname, "loaders/textLoader.js") }],
      },
    ],
  },
};

```

### 7. webpack build

```
npx webpack build  
```

### 8. 動作確認

```
node ./dist/bundle.js
// Hello John こんにちは
```

loaderによってtxtファイルをbundleすることができ
先ほどのエラーは消えていることがわかります。

## 4. pluginを作ってみよう

ここからはpluginを作成し```hello.txt```の代わりに新しく作成する```overwrite.txt```を上書きするpluginを作成したいと思います。

### plugin/overwrite.txtの作成

```plugin/overwrite.txt
上書きしたよ
```

### 2. plugin/textplugin.jsの作成

ここではtxtファイルが読み込まれるときに作用してoverwrite.txtに置換してしまうというpluginを作成します。

```javascript:plugin/textplugin.js
const path = require("path");

module.exports = class MessagePlugin {
  apply(compiler) {
    compiler.hooks.normalModuleFactory.tap("messagePlugin", (factory) => {
      // ファイルのインポートのための名前解決の手前でフック
      factory.hooks.beforeResolve.tapAsync(
        "messagePlugin",
        (resolve, callback) => {
          // 特定のファイル名のとき読み込むファイルを変える
          if (/\.txt$/.test(resolve.request)) {
            resolve.request = path.resolve(__dirname, "overwrite.txt");
          }
          callback();
        }
      );
    });
  }
};
```

### 3. webpack.config.jsの修正
 ```webpack.config.js```を修正して先ほど作成した
```textplugin.js```を読み込むようにします。


```javascript:webpack.config.js
const path = require("path");
//下記を追加
const MessagePlugin = require("./plugin/textplugin")
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  //下記を追加
  plugins: [
    new MessagePlugin()
  ],

  module: {
    rules: [
      {
        test: /\.txt$/i,
        use: [{ loader: path.resolve(__dirname, "loaders/textLoader.js") }],
      },
    ],
  },
};
```

### 4. webpack build
```
npx webpack build  
```

### 5. 動作確認
```
node ./dist/bundle.js
// Hello John 上書きしたよ
```
pluginによってファイルの読み込み時に置換されて
「こんにちは」が「上書きしたよ」になっていることが確認できたと思います。