カテゴリー: FrontEnd

Vue on Rails で作ったアプリを振り返ってみる

はじめに

個人的にRailsで作成していたWebアプリケーションのフロントエンド部分をVue.jsに切り出しました。
今回は私の使い方を振り返りつつ、記事にしたいと思います。
ドキュメントや他サイト様の記事を参考に、我流が入っている部分もあるので、ベストとは言えないかと思いますが、何かしらの気づきになれば良いな、と思います。

以前書いた、Vue.jsとRailsでTODOアプリのチュートリアルがベースにはなっています。

全体的なこと

ディレクトリ構成

RailsのWebpackerでVue.jsをインストールすると、 `javascript` ディレクトリができるので、その下をVue.js専用としています。

javascript
    ├── components
    │   ├── commons
    │   │   ├── alert.vue
    │   │   └── loading.vue
    │   ├── errors
    │   │   └── not_found.vue
    │   ├── layouts
    │   │   ├── default.vue
    │   │   ├── footer.vue
    │   │   └── header.vue
    │   ├── .....
    │   │   └── # 各ページのコンポーネント
    │   │   └── # Railsのビューを参考
    │   │
    │   └── .....
    │   
    ├── packs
    │   └── main.js
    ├── router
    │   └── router.js
    ├── store
    │   ├── modules
    │   │   ├── alert.js
    │   │   ├── auth.js
    │   │   └── loader.js
    │   └── store.js
    └── utils
        └── requests.js

エントリーポイントは `main.js` になっており、Railsのビューでは `javascript_pack_tag` から呼んでいます。

各ディレクトリの私的な決めは後ほど記載したいと思います。

CSSフレームワーク

普通に使うならば、CSSや画像などのディレクトリもあった方が良いと思います。
ただ、今回はVuetifyを使用したため、自分で書くCSSはコンポーネント内で scope させたスタイルを当てました。

Vuetify自体の使用感としては、Vue.jsのメソッドなどを使用しながらマテリアルデザインっぽいスタイルで簡単に作れました。

ただ、Vuetifyのカスタムタグやプロパティを使うので、少し変えたいな、と思ったとき苦労します。

Capistranoでのデプロイ

Capistranoを使用すれば、特別な記述なくVue.jsを適用できます。
ただ、 `asset:precompile` の時間が長くなり、貧弱なサーバーだとメモリ不足になります。
(AWSのt2.microでは度々起こりました。)

一旦、Swap領域を設けて対応しました。
1分でできる!AmazonEC2のmicroインスタンスでswap領域を作る

axiosのラッパー

utils ディレクトリは汎用的なメソッドなんかを入れておこうと思っていますが、現状はHTTPクライアントaxiosのラッパーしか入っていません。。

こちらの記事を参考に作成させていただきました。
vue.js vuex入門 開発で最低限必要そうなこと
Vue.jsとRailsの最適な融合を考える

import axios from 'axios'

export default {
  request (method, url, options) {
    var promise = null;
    var params = {};
    var headers = {};

    if (options.params) {
      // リクエストパラメーターのセット
      params = options.params;
    }
    if (options.headers) {
      // カスタムヘッダーのセット
      headers = options.headers;
    }
    if (options.auth) {
      // ヘッダーにAuthorizationをセット
      var authenticateToken = localStorage.getItem('AuthenticationToken');
      var authorization_header = { Authorization: authenticateToken }
      headers = Object.assign(headers, authorization_header);
    }
    // RailsのCSRFトークンをセット
    const token = document.getElementsByName('csrf-token')[0].getAttribute('content');
    headers['X-CSRF-TOKEN'] = token;

    promise = axios({
      method: method,
      url: url,
      data: params,
      headers: headers
    });
    promise.catch(function() {
      return console.log(promise);
    });
    return promise;
  },
  get (url, options) {
    return this.request('get', url, options);
  },
  post (url, options) {
    return this.request('post', url, options);
  },
  patch (url, options) {
    return this.request('patch', url, options);
  },
  delete (url, options) {
    return this.request('delete', url, options);
  }
}
  • パラメーターのセット
  • カスタムヘッダーのセット
  • Authorizationヘッダーのセット
  • X-CSRF-TOKENヘッダーのセット

下記のような感じで使います。

var params = { id: 1 };
request.post('/posts', { params: params, auth: true })
    .then((response) => {
  console.log('成功しました');
}, (error) => {
  console.log('失敗しました');
});

Vuex

状態管理としてVuexを使用しました。
まだVuexは記事にしたことがなかったので、今後記事にしたいと思います。

コンポーネントを使用していて分かりにくいのは、コンポーネント間のプロパティの受け渡しです。

コンポーネント間で共有されるようなプロパティは、Vuexで管理した方が良さそうな感じです。
(書いていて思ったのは、アラートの表示フラグも状態管理していますが、別にこれはコンポーネントでも良かったな、と思っています。)

Vuexは名前空間を切れるので、分類ごとにファイルを分けています。

ログインの状態管理

簡略化しましたが、ログイントークンなんかをローカルストレージに保存しているとすると、下記のような感じで実装しました。

import Vue from 'vue/dist/vue.esm.js'
import Vuex from 'vuex'
import request from '../../utils/requests'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
    loggedIn: false
  },
  mutations: {
    login(state, token) {
      localStorage.setItem('Token', token);
      state.loggedIn = true;
    },
    logout(state) {
      localStorage.removeItem('Token');
      state.loggedIn = false;
    }
  },
  actions: {
    login ({ commit }, payload) {
      var options = {
        params: {
          email: payload.data.email,
          password: payload.data.password
        }
      };
      request.post('/v1/login', options).then((response) => {
        commit('login', response.data.token);
        payload.router.push('/');
      }, (error) => {
        console.log('error!!');
      });
    },
    logout({ commit }, payload) {
      request.delete('/v1/logout', { auth: true }).then((response) => {
        commit('logout');
      }, (error) => {
        console.log('error!!');
      });
      location.href = '/';
    }
  }
})

文法の詳細は割愛しますが、 `login` ミューテーションが呼ばれると、ローカルストレージにトークンを保存し、 loggedIn ステートを変更します。
logout の場合は逆です。

コンポーネントからは、下記のようにして参照します。

<html>
  <div>
    <span>{{ loggedIn }}</span>
  </div>
</html>

<script>
  import { mapState, mapActions } from 'vuex'

  export default {
    computed: {
      ...mapState(['loggedIn'])
    },
    methods: {
      ...mapActions(['logout']),
      onLogout: function() {
        this.logout({
          router: this.$router
        });
      }
    }
  }
</script>

mapState でステートを、 `mapActions` でアクションをインスタンスに取り込みます。

ただ、Vuexはブラウザリロードすると状態がリセットされてしまうので、次のRouterで逐一チェックするようにしました。

Router

SPAなので、基本的にはvue-routerでルーティングします。
ログイン判定もVue.jsのナビゲーションガードを使用することで、各ページに遷移する前の処理を書けるので、そこで実施しています。

上述した、Vuexのリセット問題もナビゲーションガードの `beforeEach` で実施するようにしました。
簡単なサンプルです。

import Vue from 'vue/dist/vue.esm.js'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

var router = new VueRouter({
  // ....
});

router.beforeEach((to, from, next) => {
  var token = localStorage.getItem('Token');
  if (token) {
    // tokenがある場合の処理
  } else {
    // tokenがない場合の処理
  }
});

export default router

さいごに

実際に使ってみると、Vue.jsでできることが多いことが分かりました。
ただ、RailsもVue.jsもお互いにできることが多いので、明確に役割分担をしないと混乱するな、と感じています。

いずれはクライアントを完全にVue.jsで斬り離そうと考えているので、その時にまた報告したいと思います。

おすすめ書籍

    

naoki85

シェア
執筆者:
naoki85
タグ: VuejsRails

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前