単一ファイルコンポーネントを使って開発環境を強化する

Vue.js の単一ファイルコンポーネントを使えるようにします。

Vue.js はコンパイルしなくてもそのまま Web ブラウザで利用できるのも特徴なんですけど、やっぱり .vue ファイルを使ってみたい! 

単一ファイルコンポーネント

単一ファイルコンポーネントはコンポーネント毎に .vue 拡張子のファイルに分けて作成して Webpack などで一つのスクリプトファイルにバンドルします。.vue ファイルの中にはコンポーネントの JavaScript コード、HTML テンプレート、CSS をまとめて書く事ができます。

このファイルを import や require で読み込んで components に登録します。

child-comp.vue

<template>
<div class="demo">
  <span>{{ text }}</span>
</div>
</template>
<script>
export default {
  data() {
    return {
      text: 'demo'
    }
  }
}
</script>
<style scoped>
span { color: #ffbb00; }
</style>

main.js

import childComp from 'child-comp.vue'
new Vue({
el: '#app',
components: {
childComp
}
})

これは Vue の開発もだいぶ捗りそうです(•ө•)ノ

scoped CSS

スタイルシート定義に scoped を設定しておくとコンパイル時にコンポーネント内の要素につく  data-v-xxxxxx  という感じのユニークIDな属性と自動的に結び付けられます。自分でクラスを付けなくてもよくなり他のスタイルと競合する事が無くなるので基本的には scoped で書くと Good です。上の例だとコンポーネント内の span タグだけ文字色が変わります。なお、コンポーネントのルートタグには親と子の両方のIDが付くのでどちら CSS を書いても大丈夫です。

Gulp+Babel+Webpack を使う場合

モジュール化させたファイルを Webpack にまとめてもらい Babel でブラウザ用に変換して Gulp に編集をウォッチしてもらう事にします。自分で Webpack の設定とかしないといけないので vue-cli を使う場合よりも初期設定がやや面倒くさいです。

必要なモジュール

とりあえず Webpack と vue-loader というモジュールを使います。私の場合は今の開発内容だと他に下のモジュールを入れたら動きました。

  • vue-html-loader
  • vue-template-compiler(vueと同じバージョン)
  • css-loader
  • sass-loader(SASS/SCSS を使う場合)
  • node-sass(同上)
  • babel-loader
  • babel-preset-es2015

とりあえず上記あたりの開発に必要なモジュールを package.json に記述するか --save-dev を付けてインストールします。実際にアプリで使うライブラリの場合は --save でインストールします。

足りなければ足りないとエラーが出るはずなのです(っ'o'c)

gulpfile.js を書く

var gulp = require('gulp');
var webpack = require('webpack');

Webpack 自体凄い沢山設定があって覚えるの大変そうですね(´•ω•`)

var webpackBuild = function (opt) {
    var plugins = [
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: opt.map ? true : false,
            compress: {
                warnings: false
            },
        }),
    ];
    webpack({
        entry: './src/js/main.js',
        output: {
            filename: 'bundle.js',
            path: "./dist/js/",
        },
        plugins: opt.min ? plugins : [],
        module: {
            rules: [{
                    test: /\.vue$/,
                    exclude: /node_modules/,
                    loader: 'vue-loader',
                    options: {
                        loaders: {
                            scss: 'vue-style-loader!css-loader!sass-loader?includePaths[]=./src/sass/'
                        }
                    }
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    loader: 'babel-loader',
                }
            ]
        },
        devtool: opt.map ? 'source-map' : '',
        resolve: {
            extensions: ['.js', '.vue']
        }
    }, function (error, stats) {});
}
gulp.task('webpack.watch', function() {
    webpackBuild({ map: false, min: false });
});

.vue ファイルの中で SCSS の使用と変数定義用のファイルを @import を出来るようにしました。

watch タスクに登録します。

gulp.task('watch', function() {
    gulp.watch(['./src/**/*.js', './src/**/*.vue', './src/**/*.scss'], ['webpack.watch']);
});

CSSをファイルに抽出する

Webpack テンプレートの設定を参考にして、.vue の中の CSS コードを抽出してファイルに吐くようにしてみました。vue-loader のページにも載っています

var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

var webpackBuild = function (env) {
  var webpackBaseConfig = {
    cache: true,
    devtool: 'source-map',
    entry: {
      app: './src/main.js',
/* 自分のコード以外をまとめる */
      vendor: ['vue', 'axios', 'es6-promise']
    },
    output: {
      path: './dist',
      filename: '[name].js',
      publicPath: 'dist'
    },
    resolve: {
      extensions: ['.js', '.vue'],
      alias: {
        '@': path.resolve('src'),
        'vue$': 'vue/dist/vue.common.js'
      }
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          loader: 'vue-loader',
          include: [path.resolve('src')],
          options: {
            /* vue-loader のオプション CSSを抽出する */
            loaders: {
              'css': ExtractTextPlugin.extract({
                use: ['css-loader'],
                fallback: 'vue-style-loader',
              }),
              'scss': ExtractTextPlugin.extract({
                use: [{
                  loader: 'css-loader'
                },
                {
                  loader: 'sass-loader',
                  options: {
                    includePaths: [path.resolve('src/sass')]
                  }
                }],
                fallback: 'vue-style-loader',
              })
            },
          }
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          include: [path.resolve('src')],
        },
      ]
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify(env)
        }
      }),
      /* ライブラリを共有ファイルにする */
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        filename: 'vendor.bundle.js',
        minChunks: Infinity,
      }),
      /* JavaScriptを圧縮する */
      new webpack.optimize.UglifyJsPlugin({
        /* ソースマップがある場合はここでも指定 */
        sourceMap: true,
        compress: {
          warnings: false
        },
      }),
      /* CSSを抽出する */
      new ExtractTextPlugin({
        filename: './css/app.bundle.css'
      }),
      /* ローダー用オプション ロードファイルの改行スペース省くだけ? ↓かどっちか */
      /*new webpack.LoaderOptionsPlugin({
        minimize: true,
      }),*/
      /* CSSの最適化 */
      /*new OptimizeCSSPlugin({
        cssProcessorOptions: {
          safe: true,
        },
      }),*/
    ],
  }
  webpack(webpackBaseConfig, function (error, stats) {});
};

gulp.task('webpack.build', function () {
  webpackBuild('production');
});
gulp.task('webpack.dev', function () {
  webpackBuild('development');
});

ちょっと遅いのでやっぱりホットリロード神ですね(っ'ω'c)ෆ

コードを編集した時の反映が遅くてだんだんイライラしてきた場合は vue-cli を使ってみましょう。

vue-cliを使う場合

vue-cli を使うととても簡単にセットアップが出来ます。Webpack や ホットリロードの設定が最初からされててフォルダ構成にもあんまり悩まなくて良いです。単一ページ構成のサイトや SPA サイトを最初から作るならこれを使うとサクッと始められます。

$ npm install -g vue-cli
$ vue init <template-name> <project-name>
$ cd <project-name>
$ npm install

ただ設定を書き換えてエラーが起きたりとか何かトラブルがあった場合ビルド出来なくなってしまうので、ライブラリを追加したりアップデートさせたい場合には Webpack のビルド設定や package.json の記述方法をちょっとだけ覚えておいたほうが良いかもです。

開発時のAPIのクロスドメイン対策

開発モードでは Node.js のサーバーを使って開発します。この時ホスト名はデフォルトだと localhost:8080 になりますが、本来は同じドメインにある API から JSON を取得するためにはプロキシを設定すると良いです。

config/index.js

proxyTable: {
  '/api': {
    target: 'http://example.com',
    changeOrigin: true,
    pathRewrite: {
      '^/api': '/piyo/api'
    }
  }
}

これで http://example.com/piyo/api の JSON が /api で取得出来るようになります。

非SPAサイトでホットリロード設定を使う

Vue-cli のホットリロード最高かよ!非スパでホットリロードを使う記事も書きました。こちらから

まとめ

ところで私のサイトは CMS に MODX を使っていて既に少しづつ Vue を取り入れています。ゲット用のAPI を作って SPA にするか Pjax と組み合わせてみるか考えてますが、バックエンドの記事管理やデータ作成を MODX で行って、フロントは Vue.js でという感じにサイトを改良していく予定です。

とりあえず今まで何も考えずに jQuery で作っていた所はネイティブや Vue コンポーネントにしました。Vue は導入する場合全部作りなおさないとダメというわけじゃなくて、すこしづつ移行できるっていうのもいいですねー(๑'ᴗ'๑)

jQuery は jQuery で便利だし予め考えてページを作っておけば Vue との共存も問題なさそうなので、必要なページだけ読み込んでいく感じにしようかなーと思います。