【2019年05月現在】Webpack4でVue.js単一ファイルコンポーネントの作り方

**間違ってたらご指摘お願いします**:bow:

参考

- [【2018年】Webpack4でVue.js単一ファイルコンポーネントの作り方 - Qiita](https://qiita.com/INM/items/0dd60b2d4c9af5d4c699)
- [Getting Started | Vue Loader](https://vue-loader.vuejs.org/guide/)
- [Simple Todo App with Vue](https://codesandbox.io/s/o29j95wx9)



# サンプル

サンプルとして簡単なカウンターを作ってみました

【[コードはこちら](https://github.com/okumurakengo/vue_webpack)】

![MuXJvH6i4r.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/06a076ec-d437-d460-d118-e1e157f54957.gif)


```bash:bash
yarn init -y
yarn add -D webpack webpack-cli vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env

# または、

npm init -y
npm i -D webpack webpack-cli vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env
```

```:フォルダ構成
.
├ src/
│  ├ components
│  │  └ Btn.vue
│  ├ App.vue
│  └ index.js
├ .babelrc
├ index.html
├ package.json
└ webpack.config.js
```

```webpack.config.js
const VueLoaderPlugin = require("vue-loader/lib/plugin")

module.exports = {
    mode: "development",
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: "vue-loader",
            },
            {
                test: /\.js$/,
                loader: "babel-loader",
            },
            {
                test: /\.css$/,
                use: [
                    "vue-style-loader",
                    "css-loader",
                ],
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ],
    resolve: {
        extensions: [".vue", ".js"],
        alias: {
            "vue$": "vue/dist/vue.esm.js"
        }
    },
}
```

```json:.babelrc
{ "presets": ["@babel/preset-env"] }
```

```src/index.js
import Vue from "vue"
import App from "./App"

new Vue({
    el: "#app",
    template: "<App/>",
    components: { App }
})
```

```src/App.vue
<template>
    <div id="app" class="container">
        <h1>My Counter</h1>
        <hr>
        <p class="count">{{ count }}</p>
        <Btn class="blue" @handleCount="doCount('up')">Up</Btn>
        <Btn class="green" @handleCount="doCount('down')">Down</Btn>
    </div>
</template>

<script>
import Btn from "./components/Btn"

export default {
    components: {
        Btn
    },
    data () {
        return {
            count: 0,
        }
    },
    methods: {
        doCount(countType) {
            this.count += (countType === "up" ? 1 : -1)
        },
    },
}
</script>

<style scope>
.container {
    text-align: center;
    width: 500px;
}

.count {
    font-size: 35px;
    margin: 10px;
}
</style>
```

```src/components/Btn.vue
<template>
    <button @click="_handleCount">
        Count <slot></slot>!
    </button>
</template>

<script>
export default {
    methods: {
        _handleCount() {
            this.$emit("handleCount");
        }
    }
}
</script>

<style scope>
button {
    border-radius: 5px;
    cursor: pointer;
    font-size: 15px;
    height: 35px;
    width: 40%;
    color: #fff;
    transition: opacity .15s;
}
button:hover {
    opacity: .8;
}
button.blue {
    background-color: #007bff;
    border-color: #007bff;
}
button.green {
    background-color: #28a745;
    border-color: #28a745;
}
</style>
```

```index.html
<!DOCTYPE html>
<meta charset=utf-8>
<title>sample</title>
<script src=dist/main.js defer></script>
<div id=app></div>
```

```bash:bash
yarn webpack

#または、

npx webpack
```

`index.html`をブラウザで開くと動きが確認できます

# Sassを使う

`sass-loader`、`node-sass`を追加します

```bash
# 全部installする場合
yarn add webpack webpack-cli vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env sass-loader node-sass
```

```diff:webpack.config.js
  const VueLoaderPlugin = require("vue-loader/lib/plugin")
  module.exports = {
      mode: "development",
      module: {
          rules: [
              {
                  test: /\.vue$/,
                  loader: "vue-loader",
              },
              {
                  test: /\.js$/,
                  loader: "babel-loader",
              },
              {
-                 test: /\.css$/,
+                 test: /\.scss$/,
                  use: [
                      "vue-style-loader",
                      "css-loader",
+                     "sass-loader",
                  ],
              },
          ]
      },
      plugins: [
          new VueLoaderPlugin()
      ],
      resolve: {
          extensions: [".vue", ".js"],
          alias: {
              "vue$": "vue/dist/vue.esm.js"
          }
      },
  }
```

```scss:vue
<style lang="scss" scope>
body {
    .container {
        text-align: center;
        width: 500px;
    }
}

/* ... */
</style>
```

---

- ソースマップを有効にする

```diff:webpack.config.js
  const VueLoaderPlugin = require("vue-loader/lib/plugin")
  module.exports = {
      mode: "development",
      module: {
          rules: [
              {
                  test: /\.vue$/,
                  loader: "vue-loader",
              },
              {
                  test: /\.js$/,
                  loader: "babel-loader",
              },
              {
+                 test: /\.scss$/,
                  use: [
                      "vue-style-loader",
+                     {
+                         loader: "css-loader",
+                         options: { sourceMap: true },
+                     },
+                     {
+                         loader: "sass-loader",
+                         options: { sourceMap: true },
+                     },
                  ],
              },
          ]
      },
      plugins: [
          new VueLoaderPlugin()
      ],
      resolve: {
          extensions: [".vue", ".js"],
          alias: {
              "vue$": "vue/dist/vue.esm.js"
          }
      },
  }
```

<img width="510" alt="Screen Shot 2019-05-19 at 20.35.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/235640ed-14ff-4cdf-9405-218ef53a224d.png">

source mapが確認できました

# PostCSSを使う

`postcss-loader`を追加します。今回は例として`postcss-nested`で試してみます

```bash
# 全部installする場合
yarn add webpack webpack-cli vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env postcss-loader postcss-nested
```

```webpack.config.js
// ...
{
    test: /\.css$/,
    use: [
        "vue-style-loader",
        {
            loader: "css-loader",
            options: { importLoaders: 1 }
        },
        {
            loader: "postcss-loader",
            options: { plugins: [ require("postcss-nested") ] },
        },
    ],
},
// ...
```

```scss:vue
<style scope>
body {
    h1 {
        color: blue;
    }
}
</style>
```

---

↑の設定でPostCSSを用いたネスト記法を使ってcssを書けるようになりました、わかりやすくsource mapもつけてみます

```webpack.config.js
// ...
{
    test: /\.css$/,
    use: [
        "vue-style-loader",
        {
            loader: "css-loader",
            options: {
                sourceMap: true,
                importLoaders: 1
            },
        },
        {
            loader: "postcss-loader",
            options: { 
                sourceMap: true, 
                plugins: [ require("postcss-nested") ],
            },
        },
    ],
},
// ...
```

![Screen Shot 2019-05-19 at 20.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/0871362e-4ee3-5b2e-b07e-bf396f9a2e60.png)

# Pugを使う

`pug`、`pug-plain-loader`を追加します

```bash
# 全部installする場合
yarn add webpack webpack-cli vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env pug pug-plain-loader
```

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

```vue:vue
<template lang="pug">
div
  h1 Hello world!
</template>
```

<img width="612" alt="Screen Shot 2019-05-19 at 21.13.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/372aff20-168b-634b-6d69-8881b3140385.png">

# Hot Reload する

参考：https://vue-loader.vuejs.org/guide/hot-reload.html

`webpack-dev-server --hot`とするとうまくいくようです

```bash
# 全部installする場合
yarn add webpack webpack-cli webpack-dev-server vue vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env
```

```index.html
<!DOCTYPE html>
<meta charset=utf-8>
<title>sample</title>
<script src=main.js defer></script>
<div id=app></div>
```

```webpack.config.js
const VueLoaderPlugin = require("vue-loader/lib/plugin")

module.exports = {
    mode: "development",
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: "vue-loader",
            },
            {
                test: /\.js$/,
                loader: "babel-loader",
            },
            {
                test: /\.css$/,
                use: [
                    "vue-style-loader",
                    "css-loader",
                ],
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ],
    resolve: {
        extensions: [".vue", ".js"],
        alias: {
            "vue$": "vue/dist/vue.esm.js"
        }
    },
    devServer: {
        compress: true,
        port: 9000,
        open: true
    },
}
```

```bash
yarn webpack-dev-server --hot
```

![B9G3batcIB.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/d113f81c-01fd-39cd-8b9e-93badee017f9.gif)

ページがリロードされることなく、ファイル保存すると、コンポーネントも更新されていることが確認できました。

※`--hot`をつけないと、ファイル保存するとページがリロードされるという動きでした。

---

最後まで読んでいただいてありがとうございました。m(_ _)m