webpack-dev-serverでHRMを実装してみた

## はじめに
HRMというファイルの変更を検知して、ブラウザを更新させずにその変更を適用させる方法があると知った。
わかりやすくいうと、cssでcolorを`black`から`red`にしてcssファイルを保存したら、ブラウザを更新させることなく、色が変わっているということ。
再読込させれば、ファイル全体を読む必要があるため時間もかかるけど、変更した箇所だけを更新してくれるため、作業時間が一気に減るよね。
ちょっとやってみよう!!

## 環境設定
#### 1.参考

- [ベーシックなWebpackの導入と設定](https://qiita.com/senou/items/eaa43d75287fca5b33aa)
- [webpack4の導入とwebpack-dev-serverで自動ビルド](https://qiita.com/kkenya/items/01e4c2704eb83fcb6a7f)

#### 2.作業ファイル作成

```
$ mkdir work 
$ cd work
```

#### 3.`webpack`の導入

```
$ yarn init
question name (webpack-dev-server): WDS
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private:

$ yarn add -D webpack webpack-cli

$ ls
node_modules  package.json  yarn.lock
```

#### 4.バンドルの準備
`webpack`はjsファイルの複雑な依存関係を解消して、1つのjsファイルにまとめてくれる。だから、作業用のファイル、出力用のファイルの置き場を作る

```
$ mkdir src dist
$touch dist/index.html src/index.js
```

今回は、作業用ファイルの置き場(src)、出力用のファイル置き場(dist)にする

#### 5.コードの記入

```src/index.js
function component() {
  let element = document.createElement('div');

  // Lodash, currently included via a script, is required for this line to work
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}

document.body.appendChild(component());
```

```dist/index.html
<!doctype html>
<html>
  <head>
    <title>Getting Started</title>
    <script src="https://unpkg.com/lodash@4.16.6"></script>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>
```

コードは`webpack`の[チュートリアル](https://webpack.js.org/guides/getting-started/)から
`src/index.js`のコードを説明すると、`<div>Hello,webpack</div>`をしただけ。

#### 6.コードをprivateにする
コードを公開しないように`main`を消して、`private: true`を追加。

```package.json
{
  "name": "WDS",
  "version": "1.0.0",
  "main": "index.js", //delete
  "private": true,　//add
  "license": "MIT",
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2"
  }
}
```

#### 7.`lodash`のインストール

`dist/index.html`にあるように、今、`lodash`というモジュールを外部から参照して使用している。
こういう風に`<script>`でurlを書いてあると編集するとき見づらいから、インストールしよう。

```
$ yarn add -D lodash
```

外部を参照する必要がなくなったことで、`src/index.js`と`dist/index.html`は次のように変わる

```src/index.js
import _ from 'lodash';

  function component() {
    let element = document.createElement('div');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    return element;
  }

  document.body.appendChild(component());
```

```dist/index.html
<!doctype html>
<html>
  <head>
    <title>Getting Started</title>
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>
```

しれっと`dist/index.html`の`<body>`の`<script>`の`src`が`main.js`に変わっていることに注意

#### 8.ビルド

```
$ npx webpack
```
上記コマンドで、`webpack`が実行される。


![Screenshot from 2018-11-06 10-50-08.png](https://qiita-image-store.s3.amazonaws.com/0/218398/856fff86-cf30-0bcd-d1f4-82252324c16e.png)

WARNINGが出てるけど、あとで修正するため、現時点では放置して大丈夫

#### 9.webpack.config.jsの設定
現在、`webpack`の設定は`package.json`でやってるけど、より細かい設定をするためにも`webpack.config.js`を使用する。

```
$ touch webpack.config.js 
```

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

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};
```

現時点では、`src/index.js`を読み込んで、`dist/main.js`で出力ってことぐらいしか書いてないけどね。

新しい設定でbuildしてもらうためにも、次のコマンドを

```
$ npx webpack --config webpack.config.js
```
![Screenshot from 2018-11-06 10-59-01.png](https://qiita-image-store.s3.amazonaws.com/0/218398/b285a8f1-8c63-ef1b-5243-56b17e0baec3.png)

#### 10.WARNINGの解消
`npx`の実行時に表示されていた`WARNING`は`mode`を設定していないという内容。
`mode`には2種類あって、`development`はコンパイルが早い、`production`はコードの圧縮とかをしてくれるといった特徴がある。
まぁ、`development`で十分かな。

```webpack.config.js
const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist")
  }
};
```

#### 11.スクリプトの作成
これでほとんどの設定は終わったけど、ブラウザでどのように表示されるかを確かめるためには、ファイルの変更のたびに`npx`を使わなければならない。
`package.json`にスクリプトを作成することで、その必要がなくなる。

```package.json
{
  "name": "WDS",
  "version": "1.0.0",
  "private": true,
  "license": "MIT",
  "scripts": {
    "build": "webpack --watch",
    "_comment": "watchオプションを追加することで、ファイルの変更のたびにwebpackが実行される"
  },
  "devDependencies": {
    "lodash": "^4.17.11",
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2"
  }
}
```

`"_comment"`はコメントアウトの代わり。
JSONってコメントアウトできないのね。

これであとは変更のたびに、`yarn build`すれば変更内容が適用される。

以上で設定は終わり。
いまがどんな感じで表示されるかは`~/dist/index.js`をブラウザで確認。

## webpack-dev-serverの導入
#### 0.webpack-dev-serverについて
`webpack-dev-server`があれば、コードを変更したときに、自動がbuildがされる。
つまり、`yarn build`をコンソールで使用したあとは、放置して、ずっとコードを見てればいいってこった。

#### 1.webpack-dev-serverのインストール

```
$ yarn add -D webpack-dev-server
```


#### 2.スクリプトの追加
コマンドでwebpack-dev-serverが起動するようにする。

```package.json
{
  "name": "WDS",
  "version": "1.0.0",
  "private": true,
  "license": "MIT",
  "scripts": {
    "build": "webpack --watch",
    "_comment": "watchオプションを追加することで、ファイルの変更のたびにwebpackが実行される",
    "start": "webpack-dev-server"
  },
  "devDependencies": {
    "lodash": "^4.17.11",
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}
```

#### 3.webpack-dev-serverの設定
これでも十分便利だけど、webpack-dev-serverに設定をさらに追加することでもっと便利になる。
`webpack.config.js`に記入しますか。

```webpack.config.js
const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js", // このファイルを読み込む
  output: {
    filename: "main.js", //main.jsに出力
    path: path.resolve(__dirname, "dist") //pathはdist
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"), //distがサーバーの起点となる(絶対パス)
    port: 8080, //他のサーバーとポートが重ならないように
    open: true //start実行時にブラウザに開く
  }
};
```

portやopenは設定しなくても大丈夫だけど、`contentBase`だけは必ず設定する。
そうじゃないと、`webpack-dev-server`が読み込みすらしてくれない。

`dist/index.html`を表示しているブラウザと`src/index.js`を開いたエディターを並べて、変更が適用されているかを確認して、できてればOK!!

`webpack-dev-server`のことをもっと知りたい人は、[webpack4対応webpack-dev-serverの主要な設定オプション(CLI,webpack.config.js)の意味と挙動](https://qiita.com/riversun/items/d27f6d3ab7aaa119deab)を見てみることをオススメ。

## HMRの導入

やってみたかったけど、なかなかうまく行かなかったため、あとで記事にします。