# Vue.js + Webpack

# はじめに

Node.js で Vue.js アプリケーションを開発する際、@vue/cliを使用すれば一発で必要なツールが揃う。
特に@vue/cli 3になって Webpack 等の各種設定がラップされ、デフォルトの設定のままで快適に開発できるようになっている。

本稿では Webpack と Babel で各種設定をしながらビルド環境を整える方法をまとめる。

# プロジェクトの作成

$ npm init
$ mkdir src
$ mkdir src/components

以下のようにファイルを作成する。

$ touch .babelrc.js webpack.config.js /
      src/App.vue src/index.html src/main.js /
      src/components/HelloWorld.vue
/
├ .babelrc.js
├ package.json
├ webpack.config.js
└ src
  ├ App.vue
  ├ index.html
  ├ main.js
  └ components
    └ HelloWorld.vue

# 各種ツールのインストール

# Vue 関連

  • vue
    • Vue.js 本体
  • vue-loader
    • 単一ファイルコンポーネントで書かれた.vueファイル内の<template><style>を各種 loader に関連付ける
    • vue-template-complierと一緒に使う
  • vue-template-compiler
    • .vueファイルを Vue 2.0 ベースのレンダー関数に変換する
    • vue-loaderと一緒に使う

# JS 関連

  • babel-loader
    • Webpack でバンドルする時に babel でトランスパイル
  • @babel/core
  • @babel/preset-env
    • 最新版の JavaScript をES5 にトランスパイルするためのプリセット

# CSS 関連

  • css-loader
    • Webpack でバンドルする時に、 .cssファイルと.vue内の CSS を集めて文字列に変換
  • vue-style-loader
    • css-loader で文字列に変換した CSS を HTML ドキュメントの<head>タグ内の<style>タグに埋め込む
    • css-loaderと一緒に使う

# HTML 関連

  • html-webpack-plugin
    • バンドルした JavaScript ファイルをindex.html内の<head>タグ内の<script>タグに埋め込み、distフォルダに出力する

# Webpack 関連

  • webpack
    • ES Modules の依存関係を解決してファイルをバンドルする
    • node_modules内のライブラリ (CommonJS、AMD) をバンドル
    • 各種 loader で.js以外のファイルも読み込める
  • webpack-cli
    • Webpack を CLI で動かすために必要
  • webpack-dev-server
    • 開発用サーバを立てるために必要

これらを npm でインストールする。

& npm i --save-dev vue vue-loader vue-template-compiler \
                   babel-loader @babel/core @babel/preset-env \
                   css-loader vue-style-loader \
                   html-webpack-plugin \
                   webpack webpack-cli webpack-dev-server \ 

-devオプションをつけることで、package.json内のdevDependenciesに 関連付けさせている。

# Webpack の設定

webpack.config.jsを編集していく。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  devServer: {
    open: true,
    hot: true,
  },
  entry: './src/main.js',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader'
      },
      {
        test: /\.vue$/,
        use: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader']
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new VueLoaderPlugin()
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src/'),
      vue$: 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', 'json']
  }
};

# devServer

webpack-dev-serverに関連する設定を指定する。

module.exports = {
  //...
  devServer: {
    open: true
  }
};
  • open - webpack-dev-serverを立ち上げたとき、ブラウザを自動で開く
  • hot - ファイルを変更するとアプリケーションを自動で更新する

ホットリロード便利。

# entry

依存関係を決定する上での出発点であるエントリーポイントを指定する。

以下のように複数ファイルを指定することも可能。

module.exports = {
  //...
  entry: {
    'app': './src/app.js',
    'main': './src/main.js'
  }
};

Entry Points | webpack | Object Syntax

# module

任意の拡張子のファイルに各種ローダーを割り当てる。

testプロパティ内の正規表現に合致するファイルをuseプロパティで指定したローダーで処理する。

Babel 7 以降ではデフォルトでnode_modulesを除外してくれるらしいので、exclude: /node_modules/を明示する必要はなくなった。

Upgrade to Babel 7 (API) · Babel | Config changes

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        // babel 7 ではデフォルトで除外されるので Webpack 側での設定は不要
        // exclude: /node_modules/
        // .babelrc.js で設定するオプションをここに書くこともできる
        options: {
          presets: ['@babel/preset-env']
        }
      },
      {
        test: /\.vue$/,
        use: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader']
      }
    ]
  }
};

ある程度の規模以上になり Babel の設定項目も増えてくるとwebpack.config.jsも肥大してくるので、Babel の設定は.babelrc.jsに書いた方が良さそう。

# plugins

各種プラグインを設定する。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new VueLoaderPlugin()
  ]
};

html-webpack-plugin はデフォルトで./src/index.ejsを見に行くので、./src/index/htmlを見に行かせるよう指定する。

vue-loaderを使うためにはプラグインに追加する必要がある。

# resolve

モジュールの依存関係を解決する際の振る舞いを設定する。

const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src/'),
      vue$: 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', 'json']
  }
};

エイリアスを設定することでパスを省略してimportできる。

// エイリアスなし
import '../src/components/LikeButton.vue'
// エイリアスあり
import '@/components/LikeButton.vue'

@vue/cliでは以上のようなエイリアスがデフォルトで設定されている。

vue-cli/base.js at dev · vuejs/vue-cli

また、extensionsプロパティに拡張子の配列を渡すことで拡張子を省略してimportできる。

import '@/components/LikeButton';

異なる拡張子の同名ファイルがあった場合、配列の前にある要素が優先される。

Module Resolution | webpackに依存関係の解決の仕方について詳しく書いてある。

# 各ファイルを準備

Hello World するのに必要なファイルを準備する。

# .babelrc.js

これで最新版の ECMAScript を ES5 に変換できる。

module.exports = {
  presets: ['@babel/preset-env'],
};

# src/App.vue

<template>
  <HelloWorld />
</template>

<script>
import HelloWorld from '@/components/HelloWorld';

export default {
  components: {
    HelloWorld
  }
};
</script>

# src/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Hello World</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

# src/main.js

import Vue from 'vue';
import App from './App.vue';

new Vue({
  el: '#app',
  render (h) {
    return h(App);
  }
});

# src/components/HelloWorld.vue

<template>
  <div class="hello-world">
    {{ message }}
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      message: 'Hello World'
    }
  }
};
</script>

<style scoped>
.hello-world {
  font-size: 24px;
}
</style>

# webpack-dev-serverの立ち上げ

pakeage.jsonに以下のスクリプトを追加する。

+ "scripts": {
+ "serve": "webpack-dev-server --mode development"
+ }

npm run serveとすると、webpack-dev-serverlocalhost:8080で立ち上がる。

npm run serve
> webpack-dev-server --mode development

i 「wds」: Project is running at http://localhost:8080/
...

webpack.config.jsopenオプションを有効にしたので、http://localhost:8080が自動で開く。

Hello World できた。

# ビルド

rimrafをインストールする。

$ npm i --save-dev rimraf

rimrafはUnixコマンドrm -rfの Node.js 実装版。rm -rfと違って対象ディレクトリがない場合でもエラーを投げないため、ビルドする際のスクリプトを書くのに便利。

pakeage.jsonに以下のスクリプトを追加する。

+ "scripts": {
+ "build:clean": "rimraf dist"
+ "build": "npm run build:clean && webpack --mode production"
+ }

distディレクトリがあれば削除して、新たにdistディレクトリを吐き出している。

$ npm run build

とすると、distディレクトリにindex.htmlmain.jsが出力される。

# あとで書くやつ

  • TypeScript
  • Pug
  • Sass (SCSS)
  • Stylus

# 参考

Last Updated: 2019-11-27 1:19