SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

RailsでVue.jsのSFC(単一ファイルコンポーネント)を使うためにWebpackを入れてみた

f:id:yuma124:20190512165142p:plain

こんにちは。エンジニアの笹原です。

スマートキャンプではフロントエンドの開発にVue.jsを取り入れています。

Vue.jsで開発するうえで、SFC(単一ファイルコンポーネント)を利用できることは良さの一つだと思います。

今回は、Sprocketsでアセット管理を行っているRailsプロジェクトに、Webpackを入れることでSFCを使えるようにする方法を紹介します。

SFC(単一ファイルコンポーネント)とは

Single File Componentの略で、テンプレート、ロジック、スタイルを一つのファイルで管理しているコンポーネントのことです。

SFCの良さ

一つのファイルで管理することは、コンポーネントベースで画面を構成していく上で保守性を増すことに繋がります。

公式ドキュメント(単一ファイルコンポーネント — Vue.js)にも書いてあるとおり、関心事項がコンポーネントごとに分離されているような画面において、コンポーネント内のテンプレート、ロジック、スタイルは結合されているため、同一ファイルにまとまっているのが自然です。

一方で、ロジックやスタイルが別のコンポーネントに影響しないような作りにもなっていることから、それぞれを他のコンポーネントとまとめる必要性も薄れています。

SFCの実装方法

SFCで実装は、.vue 拡張子のファイル内に templatescriptstyleの3つのタグ内にそれぞれ、テンプレート、ロジック、スタイルを記述していきます。

そのため、実際にブラウザで読み込める形にするには、WebpackやBrowserifyといったビルドツールを使うことになります。

<template>
  <p>Hello World</p>
</template>

<script>
  export default {
    name: "Hello"
  }
</script>

<style scoped>
  p {
    font-size: 2em
  }
</style>

Sprocketsを利用しているRailsプロジェクトにSFCを導入する

課題と解決方法

ここで、ようやく本題に入っていくんですが、Railsのデフォルトのアセット管理ライブラリであるSprocketsはこの .vue 拡張子のビルドは行ってくれません。

そのため、新規にビルドツールを導入することになるのですが、既存のSprockets管理下のアセットも同時に移行するのは大変です。

そこで、既存のアセットは従来どおりSprockets管理下としつつ、新しくWebpackを導入した上で、Webpackがビルドした生成物を既存のSprocketsが併せて管理する方法を取りたいと思います。

RailsでWebpackを使う際に、Webpackerを利用する方法もありますが、Webpackerはconfig記述が独自の部分があるなど、Webpack単体よりも気にしなければならないことが増えそうなため、素のWebpackを利用するようにしています。

完成予想プロジェクト構成

もろもろ端折っているのもありますが、構成自体はシンプルです。

app ディレクトリ配下がRailsアプリケーションで frontend 配下が導入するWebpack管理下になります。

Webpack管理下のアセットをビルドした上で、生成物をRailsアプリケーションのSprockets管理下に配置します。

├── app/
│   ├── assets/
│   │   ├── javascripts/
│   │   │   ├── webpack/
│   │   │   │   ├── plugins.js ・・・・・・ビルド生成物
│   │   ├── stylesheets/
│   │   │   ├── webpack/             
│   │   │   │   ├── plugins.css ・・・・・ビルド生成物
├── frontend/
│   ├── src/
│   │   ├── components/
│   │   │   │   ├── Hello.vue
│   │   ├── entry.js
│   ├── webpack.config.js
├── package.json

導入手順①: 必要なnpmモジュールのインストール

まずは、nodeプロジェクトを初期化して、必要なモジュールをインストールしていきます。

$ yarn init
$ yarn add vue
$ yarn add -D @babel/core babel-loader babel-preset-env clean-webpack-plugin css-loader mini-css-extract-plugin vue-loader vue-template-compiler webpack webpack-cli webpack-merge webpack-node-externals

導入手順②: Webpackの設定

続いて、Webpackの設定ファイルを作成していきます。

普通にVueのビルド設定をしたうえで、outputのpathをRailsアプリケーションが管理している app 配下にするだけです!

シンプルな構成例を用意しておきます!

実際に使う際には、scriptやstyleでTypeScriptやSCSSを使うためのloaderなどを追加してください。

webpack.config.js

const path = require('path');
const webpack = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

function resolve (dir) {
  return path.resolve(__dirname, dir)
}

const RAILS_ASSETS_ROOT = resolve('../app/assets/');

module.exports = {
  entry: resolve('./src/entry.js'),
  output: {
    path: RAILS_ASSETS_ROOT,
    filename: 'javascripts/webpack/plugins.js',
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader'
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: file => (
          /node_modules/.test(file) &&
          !/\.vue\.js/.test(file)
        )
      },

      {
        test: /\.css/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  resolve: {
    extensions: ['.vue', '.js'],
  },
  plugins: [
    new webpack.ProvidePlugin({
      'Vue': 'vue'
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: 'stylesheets/webpack/plugins.css'
    })
  ]
};

webpack.prod.config.js

const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const baseConfig = require('./webpack.config');

module.exports =  merge(baseConfig, {
  mode: 'production',
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ['javascripts/webpack/', 'stylesheets/webpack']
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ]
});

webpack.dev.config.js

const merge = require('webpack-merge');
const baseConfig = require('./webpack.config');

module.exports =  merge(baseConfig, {
  mode: 'development',
  devtool: 'inline-cheap-module-source-map'
});

導入手順③: ビルドの実行

Webpackの設定までできたらあとはビルドするのみです。

package.jsonのscriptsにwebpackのコマンドを追加します。

{
  ...
  "scripts": {
    "dev": "webpack --progress --hide-modules --watch --config frontend/webpack.dev.config.js",    # --watchが入っているので変更を検知して自動でbuildし続けてくれます。
    "build": "webpack --progress --hide-modules --config frontend/webpack.prod.config.js"
  },
  ...
}

以下のコマンドを実行することで、Railsアプリケーションが管理しているapp配下にビルドされ、生成物がRailsから利用できるようになります!

# 開発時
$ yarn dev
# リリース時
$ yarn build

終わりに

今回はVue.jsのSFCを使うためにRailsプロジェクトにWebpackを導入してみました。

既存のRailsアプリケーションの大枠の構成を変えずに、とりあえずWebpackを入れてみることでコストをかけずに新しい技術を取り入れることができました。

規模がある程度大きくなったアプリケーションではすべてを一度にリプレースする判断も難しいので、このように一部から取り入れることで技術が合うかどうか見ていけると良いと思います。