はじめに
個人的にRailsで作成していたWebアプリケーションのフロントエンド部分をVue.jsに切り出しました。
今回は私の使い方を振り返りつつ、記事にしたいと思います。
ドキュメントや他サイト様の記事を参考に、我流が入っている部分もあるので、ベストとは言えないかと思いますが、何かしらの気づきになれば良いな、と思います。
以前書いた、Vue.jsとRailsでTODOアプリのチュートリアルがベースにはなっています。
全体的なこと
ディレクトリ構成
RailsのWebpackerでVue.jsをインストールすると、
javascript
ディレクトリができるので、その下をVue.js専用としています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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の最適な融合を考える
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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ヘッダーのセット
下記のような感じで使います。
1 2 3 4 5 6 7 | var params = { id: 1 }; request.post('/posts', { params: params, auth: true }) .then((response) => { console.log('成功しました'); }, (error) => { console.log('失敗しました'); }); |
Vuex
状態管理としてVuexを使用しました。
まだVuexは記事にしたことがなかったので、今後記事にしたいと思います。
コンポーネントを使用していて分かりにくいのは、コンポーネント間のプロパティの受け渡しです。
コンポーネント間で共有されるようなプロパティは、Vuexで管理した方が良さそうな感じです。
(書いていて思ったのは、アラートの表示フラグも状態管理していますが、別にこれはコンポーネントでも良かったな、と思っています。)
Vuexは名前空間を切れるので、分類ごとにファイルを分けています。
ログインの状態管理
簡略化しましたが、ログイントークンなんかをローカルストレージに保存しているとすると、下記のような感じで実装しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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
の場合は逆です。
コンポーネントからは、下記のようにして参照します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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
で実施するようにしました。
簡単なサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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で斬り離そうと考えているので、その時にまた報告したいと思います。