Help us understand the problem. What is going on with this article?

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

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

参考

サンプル

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

コードはこちら

MuXJvH6i4r.gif

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"
        }
    },
}
.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
yarn webpack

#または、

npx webpack

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

Sassを使う

sass-loadernode-sassを追加します

# 全部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
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"
          }
      },
  }
vue
<style lang="scss" scope>
body {
    .container {
        text-align: center;
        width: 500px;
    }
}

/* ... */
</style>

  • ソースマップを有効にする
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"
          }
      },
  }

Screen Shot 2019-05-19 at 20.35.22.png

source mapが確認できました

PostCSSを使う

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

# 全部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") ] },
        },
    ],
},
// ...
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

Pugを使う

pugpug-plain-loaderを追加します

# 全部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
<template lang="pug">
div
  h1 Hello world!
</template>

Screen Shot 2019-05-19 at 21.13.29.png

Hot Reload する

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

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

# 全部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
    },
}
yarn webpack-dev-server --hot

B9G3batcIB.gif

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

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


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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away