- 投稿日:2019-07-07T23:23:46+09:00
【Vue.js】Vuexの入り口
Vuexの学習を始めるにあたり、一体どういうものなのか大枠が分かれば学習も進みやすくなると思い、Vuexを使った基本的な状態管理の流れを書いていきます。
参考記事・文献
Vue.js入門
Vue CLIとVuexでアプリケーションの状態変化を扱うNode.jsのインストール方法、vue-cliの始め方はこちらの記事が大変参考になります
MacにNode.jsをインストール
Vue.js を vue-cli を使ってシンプルにはじめてみるVuexを始める前に
公式ドキュメントを読んでおきましょう。
始めのうちは書き方も良く分からないですが、ぼんやりと理解し全体として何が出来るのかをつかむことがすごく重要に感じました。
・Vuex とは何か?
・Vuex 入門
・コアコンセプトの5つ
これだけでも目を通しておけば何を意識すれば良いか分かり、後から少しずつ理解できてきます。Vue-cliを使いVuexを始める
Vue-cliがインストール時にまとめて追加できるので簡単かと思います。
今回はその方法でいきます。
Vue-cliでプロジェクト作成
下記のコマンドでプロジェクトを作成します。
project_nameのところは,
お好きな名前で。今回はvue_sumpleとつけました。terminal$ vue create project_nameデフォルト設定か自分で設定するか聞かれます。今回はvuexを取り入れるので、
Manually select featuresを選択。terminalVue CLI v3.9.2 ? Please pick a preset: default (babel, eslint) ❯ Manually select features取り入れられる対象のパッケージが表示されます。BabelとLinter / Formatterは初期でチェックが入っています。Vuexの他にRouterやsassを使うかなども選べます。今回はVuexにチェックを入れます。(スペースキーでチェック出来ます)
terminalVue CLI v3.9.2 ? Please pick a preset: Manually select features ? Check the features needed for your project: ◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ❯◉ Vuex ◯ CSS Pre-processors ◉ Linter / Formatter ◯ Unit Testing ◯ E2E Testingこの後何個か質問されますが、こだわりがなければ全てEnterで良さそうです。
最後に設定を保存するか聞かれます。(ここではNにしました。)terminal? Save this as a preset for future projects? (y/N)ここで作成が開始されます。
無事作成完了すると、下記の画面が表示されます。
コマンドの指示に従って、作られたディレクトリに移動しnpmを立ち上げます。terminal? Successfully created project vue_sumple. ? Get started with the following commands: $ cd vue_sumple $ npm run serve下記の画面が表示されるので、LoacalのURLをブラウザにコピペします。
terminalApp running at: - Local: http://localhost:8080/ - Network: http://192.168.3.25:8080/今回修正するファイル
・main.js
・store.js
・App.vue
この3つでvuexの大まかな流れは掴めるかと思います。main.js
初期でVuex用のスクリプトを読み込むようになっているので、ここは今回いじらなくても良いです。
main.jsimport Vue from 'vue' import App from './App.vue' import store from './store' Vue.config.productionTip = false new Vue({ store, //コンポーネントからstoreを利用できるようにする render: h => h(App) }).$mount('#app')store.js
store.jsimport Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //vuexが読み込まれている // storeをエクスポート export default new Vuex.Store({ //ここに実装を書く state: { }, mutations: { }, actions: { } })store.jsでは「state」「mutations」「actions」というものがあり、ここで状態管理をまとめて行っています。
【state】
Vueインスタンスでいう「data」オプションのようなもので、初期値としての文字列や配列・ブーリアン値などの値が入ります。【mutations】
stateの値を更新します。stateの値を更新できるのはmutationsからのみとなります。commit関数で呼び出されます。【actions】
登録されたmutationsを実行します。commit関数の引数で実行するmutationsを指定します。コンポーネントからはdispatch関数で実行できます。このようにvuexでは、「actions」で「mutations」の関数を指定し「state」の値を更新、最後にコンポーネントのDOMが書き換わるというイメージになります。
store.jsの内容を表示する為にApp.vueを書き換えていくことになります。App.vue
App.vue<template> <div> <!-- ここに実装を書く --> </div> </template> <script> export default { // ここに実装を書く } </script>実装例
例として、todoリストのタスク表示のサンプルコードを載せます。
store.jsのstateにタスク内容、それをApp.vueで表示、という流れがわかるかと思います。store.jsimport Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { tasks: [ { id: 1, name: '牛乳を買う', done: false }, { id: 2, name: 'vueの本を買う', done: true } ], }, })App.vue<template> <div> <h2>タスク一覧</h2> <ul> <li v-for="task in tasks" v-bind:key="task.id"> <input type="checkbox" v-bind:checked="task.done"> {{ task.name }} </li> </ul> </div> </template> <script> export default { name: 'App', computed: { tasks () { return this.$store.state.tasks //storeを読む }, }, } </script>長くなってしまうのでここでは画面にstate内容を表示するだけですが、例えば新しくタスク追加する機能を実装する処理をmutationsに宣言しておき、それをApp.vueで動かす処理を書く、といった流れになっていきます。
今回は以上になります。
始めはvuexは大きく漠然としたもののように感じるかもしれませんが、細分化し始めの一歩はどこになるのかを考え紐解いていくことで、少しずつ仕組みが理解できると思います。また学習が進み次第、更新、掲載していきます。
ここまでで補足や訂正などありましたら是非ご教授いただけると嬉しいです。
最後まで読んでいただきありがとうございます。
- 投稿日:2019-07-07T21:14:42+09:00
Vue.js チュートリアル for Rails エンジニア
はじめに
この記事は、普段 rails を使用して開発を行なっているエンジニアが、 Vue.js を触り始めようとする時に見たら役にたつかもしれないものです。
チュートリアルの内容
チュートリアルは、以下の2本立てで行うことで、 Vue.js に触れたことがない Rails エンジニアでも理解しやすい形にしています。すでに Vue.js に触れたことがある場合は、1を飛ばして、2から始めるのが良いかと思います。
- 簡単な Vue.js アプリケーションの開発
- Webpacker を使用した簡単な Rails + Vue.js アプリケーションの開発
使用するバージョン
- ruby: 2.6.2
- rails: 5.2.3
- webpacker: 4.0.7
- yarn: 1.16.0
1. 簡単な Vue.js アプリケーションの開発
を書こうと思ったら、すでにめちゃくちゃいいチュートリアル記事が既にあったので、そちらのリンクを掲載させていただきます。(タイトル詐欺)
Vue.js を vue-cli を使ってシンプルにはじめてみる2. Webpacker を使用した簡単な Rails + Vue.js アプリケーションの開発
1 がだいたい終わって、 Vue.js のことをだいたい理解していることが前提です。
サンプルコードはこちら で公開しています。環境構築
環境構築には、
homebrew,rbenvを使用します。インストールがまだな方は、各自インストールをお願いします。rubyのバージョン設定
$ brew update && brew upgrade ruby-build$ rbenv install 2.6.2アプリケーションを作成するディレクトリに、
cdコマンドで遷移し、下記を実行してください。$ rbenv local 2.6.2 $ ruby -v ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin18]yarn のインストール
$ brew install yarnインストールの確認(バージョンは下記のもの以上であれば基本問題ないと思います。)
$ yarn -v 1.16.0Vue.js devtools のインストール
インストールがまだな場合は、下記のページを参考にインストールしておきましょう。
Vue.js Devtoolsの導入方法と機能まとめ。Vue.jsを用いた開発を効率化させよう!プロジェクトの作成
下記コマンドを実行して、プロジェクトを作成します。
$ rails new rails-vue-app --webpack=vue --skip-turbolinks --skip-coffee※
--skip-turbolinks --skip-coffeeのオプションをつけて、今回は不要となるものを削ぎ落としています。 1実行結果のログを眺めていると、 gem のインストールの後に、
rails webpacker:installから始まる webpacker 関連のインストールが行われていることがわかります。rails webpacker:install RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environmentwebpacker のインストールが完了すると、普段の rails プロジェクトでは見られない、
app/javascriptというディレクトリが、自動で生成されているかと思います。$ cd rails-vue-app/app/javascript $ ls app.vue packs※ 今回の実装では、新規に Rails + Vue.js のプロジェクトを作ることを想定していますが、もちろん既存の Rails プロジェクトに途中から Vue.js を導入することもできます。2
それでは、これから rails アプリケーションの中身の実装を進めていこうと思います。
アプリケーションの実装
一番シンプルな実装
まずは、 rails 上で vue.js を動かす一番シンプルな実装をしていきます。
cdコマンドなどで先ほど作成したディレクトリに移動し、まずはコントローラーを作成します。
※ 今後[]はコマンドを実行するディレクトリを表します。[rails-vue-app] $ rails g controller HelloVue index --no-helper --no-assets create app/controllers/hello_vue_controller.rb route get 'hello_vue/index' invoke erb create app/views/hello_vue create app/views/hello_vue/index.html.erb invoke test_unit create test/controllers/hello_vue_controller_test.rb※ 今回は、ヘルパーやアセットファイルなど不要なものを生成しないオプションを指定しています。 3
一旦サーバーを起動して、画面が表示するかみて見ましょう。
[rails-vue-app] $ rails shttp://localhost:3000/hello_vue/index にアクセスして、下記のページが表示されると ok です。
javascript_pack_tag の設定
javascript_pack_tagでapp/javascript/packs配下にあるファイルを読み込むことができます。 (この辺は webpackerの仕様で決まっています)
今回は、読み込む対象としてhello_vue.jsを指定してみます。hello_vue.jsは webpacker のインストール時に自動生成されているファイルです。app/views/hello_vue/index.html.erb<h1>HelloVue#index</h1> <p>Find me in app/views/hello_vue/index.html.erb</p> + <%= javascript_pack_tag 'hello_vue.js' %>
hello_vue.jsの中身は、下記のような感じになっており、同じく初期作成されたapp.vueファイルを描画するようになっています。app/javascript/packs/hello_vue.jsimport Vue from 'vue' import App from '../app.vue' document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ render: h => h(App) }).$mount() document.body.appendChild(app.$el) console.log(app) })app/javascript/app.vue<div id="app"> <p>{{ message }}</p> </div> </template> <script> export default { data: function () { return { message: "Hello Vue!" } } } </script> <style scoped> p { font-size: 2em; text-align: center; } </style>これらのファイルがどう言う役割をしているのかが曖昧な場合は、もう一度 簡単な Vue.js アプリケーションの開発 の項を見てみましょう。
webpacker を使用した webpack のビルド
下記のコマンドを実行することで、
app/javascript配下の js ファイルをビルドすることができます。[rails-vue-app] $ bin/webpackこのコマンドは、内部的には下記のコマンドを実行しているのにほぼ等しいらしいです。(知らなかった)
[rails-vue-app] ./node_modules/.bin/webpack --config config/webpack/development.js※ Webpacker使うなら最低限これだけは知っておいてほしいこと から抜粋させてもらいました。
再度 http://localhost:3000/hello_vue/index にアクセスしてみると、下記のような画面になっているかと思います。
環境構築で、
Vue.js devtoolsのインストールが完了している場合、 chrome の拡張機能の部分に、Vのマークが色付きで出てきているはずです。
この時点で、一番の基本となる rails + Vue.js のアプリケーション実装が完了しました。
webpack の自動ビルド
先ほどは、
bin/webpackのビルドを行いましたが、これでは js ファイルを変更するたびに再度コマンドを叩いてビルドをする必要があります。
流石にそれは面倒なので、開発中はbin/webpackの代わりに下記コマンドを実行して、ファイルを保存するたびに自動ビルドが走るようにしておくといいでしょう。[rails-vue-app] $ bin/webpack-dev-serverVue.js devtools
せっかくなので
Vue.js devtoolsの使い方をここで確認しておきます。
使い方は簡単で、 chrome の developer ツールを開いて、 Vue のタブを選択するだけです。
Vue のタブを選択し、コンポーネントを選択してみると、内部の data などを確認することができます。
実践的な実装
先ほど実装はただ単に、作成した vue ファイルの描画を行っただけで、 controller などからのデータの受け渡しを行なっていませんでした。そこで、ここからはその点を深掘っていきます。
webpacker を使用した、 rails + vue のアプリケーションを作成する際、データの渡し方には色々あるらしいですが、今回は2通りのデータの受け渡し方でサンプルアプリケーションを作っていきます(個人的にはこの2つの渡し方は、データフローがわかりやすい)。
- HTML のデータ属性に値を設定して渡す方法
- API を使用して渡す方法
前者の方法については、下記の記事を参考にさせていただきました。
メンテ不能になったフロントエンド環境を立て直す話HTML のデータ属性に値を設定して渡す方法
まずはページを表示するためのコントローラーを、
HomeControllerという名前で作成します。$ cd rails-vue-app [rails-vue-app] $ rails g controller Home index --no-helper --no-assets create app/controllers/home_controller.rb route get 'home/index' invoke erb exist app/views/home create app/views/home/index.html.erb invoke test_unit create test/controllers/home_controller_test.rb次にコントローラー内部の実装を進めていきます。
indexメソッドのインスタンス変数として、titledescriptionそして Hash 形式のcontentsを用意します。app/controllers/home_controller.rbclass HomeController < ApplicationController def index + @title = 'Home#index' + @description = 'トップページ' + @contents = get_contents end + + private + + def get_contents + { + outer_links: [ + { + name: 'Qiitaページ', + text: 'Qiita', + url: 'https://qiita.com/t0yohei/items/d516fefaaad69b4022ec' + }, + { + name: 'ソースコード', + text: 'GitHub', + url: 'https://github.com/t0yohei/rails-vue-app' + } + ], + } + end end続いて
viewの実装です。ここでポイントとなるのが、 rails のcontent_tagヘルパーを利用して、divタグのdata属性に vue 側に受け渡したいデータ設定している点です。 vue に受け渡すデータはjson形式に変換しておきます。app/views/home/index.html.erb- <h1>Home#index</h1> - <p>Find me in app/views/home/index.html.erb</p> + <%= javascript_pack_tag 'home/index.js' %> + <%= content_tag :div, + id: "homeIndex", + data: { + title: @title, + description: @description, + contents: @contents + }.to_json do %> + <% end %>※
data属性に設定した情報は、 developer tool などを使うことで閲覧することができるので、 API 同様にユーザーのプライペート情報など、秘匿情報は公開しないようにして注意してください。表示するコンポーネントの実装
先ほど view で設定したデータを、 js 側から読み取ってみましょう。
app/javascript/packs/home/index.jsを下記の通り実装します。app/javascript/packs/home/index.jsimport Vue from "vue"; document.addEventListener("DOMContentLoaded", () => { const node = document.getElementById("homeIndex"); const props = JSON.parse(node.getAttribute("data")); console.log(props); });http://localhost:3000/home/index にアクセスして、 developer tool の console を開けてみると、
このように、 rails の view ファイルで設定したデータが、JS の Object 形式で取得できていることがわかります。
この Object データを使用して、実装を進めていきましょう。
home/index.jsでは、home/Indexというコンポーネントを render することを想定して実装を進めていきます。 render 関数の第二引数に、先ほど取得した Object のデータを設定します。app/javascript/packs/home/index.jsimport Vue from "vue"; +import Index from "../../components/home/Index.vue"; document.addEventListener("DOMContentLoaded", () => { const node = document.getElementById("homeIndex"); const props = JSON.parse(node.getAttribute("data")); + const app = new Vue({ + render: h => h(Index, { props }) + }).$mount(); + document.body.appendChild(app.$el); - console.log(props); });render 対象の、
home/Indexコンポーネントでは、受け取る props を定義しておきます。これでhome/index.jsからデータを受け取って、コンポーネント内で参照することができます。app/javascript/components/home/Index.vue<template> <div> <h1>{{ title }}</h1> <p>{{ description }}</p> <table class="contents-table"> <tr> <th>名前</th> <th>リンク</th> </tr> <tr v-for="outer_link in contents.outer_links" v-bind:key="outer_link.name"> <td>{{ outer_link.name }}</td> <td> <a v-bind:href="outer_link.url">{{ outer_link.text }}</a> </td> </tr> </table> </div> </template> <style scoped> .contents-table { border: 1px solid gray; margin: 10px; } .contents-table th, .contents-table td { border: 1px solid gray; } </style> <script> export default { props: { title: { type: String, default: () => "" }, description: { type: String, default: () => "" }, contents: { type: Object, default: () => {} } } }; </script>ページの表示
app/javascript/packs配下に追加した js ファイルを読み込ませるためには、webpack-dev-serverの再起動が必要です。webpack-dev-serverを実行中の場合は一度止めて、再度下記コマンドを実行しましょう。[rails-vue-app] $ bin/webpack-dev-serverhttp://localhost:3000/home/index にアクセスした時に、下記のようなページが表示されていると成功です。
実装のリファクタリング
とりあえず表示させることを優先で、
Index.vueに全てを書いていたので、簡単にコンポーネントの分割をしていきます。ざっとこんな感じのイメージで分割していきます。
HeaderView.vue の作成
app/javascript/components/HeaderView.vue<template> <div> <h1>{{ title }}</h1> <p>{{ description }}</p> </div> </template> <style></style> <script> export default { props: { title: { type: String, default: () => "" }, description: { type: String, default: () => "" } } }; </script>Contents.vue の作成
app/javascript/components/home/Contents.vue<template> <div> <table class="contents-table"> <tr> <th>名前</th> <th>リンク</th> </tr> <tr v-for="outer_link in contents.outer_links" v-bind:key="outer_link.name"> <td>{{ outer_link.name }}</td> <td> <a v-bind:href="outer_link.url">{{ outer_link.text }}</a> </td> </tr> </table> </div> </template> <style scoped></style> <script> export default { props: { contents: { type: Object, default: () => {} } } }; </script>Index.vue の修正
app/javascript/components/home/Index.vue<template> <div> - <h1>{{ title }}</h1> - <p>{{ description }}</p> - <table class="contents-table"> - <tr> - <th>名前</th> - <th>リンク</th> - </tr> - <tr v-for="outer_link in contents.outer_links" v-bind:key="outer_link.name"> - <td>{{ outer_link.name }}</td> - <td> - <a v-bind:href="outer_link.url">{{ outer_link.text }}</a> - </td> - </tr> - </table> + <header-view v-bind:title="title" v-bind:description="description"></header-view> + <contents v-bind:contents="contents"></contents> </div> </template> -<style scoped> -.contents-table { - border: 1px solid gray; - margin: 10px; -} -.contents-table th, -.contents-table td { - border: 1px solid gray; -} -</style> +<style scoped></style> <script> +import HeaderView from "../HeaderView.vue"; +import Contents from "./Contents.vue"; + export default { + components: { + "header-view": HeaderView, + contents: Contents + }, props: { title: { type: String, default: () => "" }, description: { type: String, default: () => "" }, contents: { type: Object, default: () => {} } } }; </script>だいぶスッキリしましたね。リファクタリングは一旦こんな感じで終わりましょうか。
念の為、再度 http://localhost:3000/home/index にアクセスして、画面がちゃんと表示されることを確認しておきましょう。API を使用して渡す方法での実装
次に、API を使用して渡す方法での実装を進めていきましょう。
今回は特に理由もないんですが、整数リテラルの分類表を作成してみます。
手順としては、
- APIを叩いて取得したデータを受け取るコンポーネントの実装
- APIエンドポイントの実装
- コンポーネントから API を叩いてデータを取得
- 取得したデータをコンポーネント内で描画
という順番で進めていきます。
APIを叩いて取得したデータを受け取るコンポーネントの実装
まずはページを表示するためのコントローラーを作成します。
$ cd rails-vue-app [rails-vue-app] $ rails g controller IntegerLiteralDescriptions index --no-helper --no-assets create app/controllers/integer_literal_descriptions_controller.rb route get 'integer_literal_descriptions/index' invoke erb create app/views/integer_literal_descriptions create app/views/integer_literal_descriptions/index.html.erb invoke test_unit create test/controllers/integer_literal_descriptions_controller_test.rbコントローラーの実装
今回は何もしないです。
View の実装
javascript_pack_tagを設定します。app/views/integer_literal_descriptions/index.html.erb- <h1>IntegerLiteralDescriptions#index</h1> - <p>Find me in app/views/integer_literal_descriptions/index.html.erb</p> + <%= javascript_pack_tag 'integerLiteralDescriptions/index.js' %>表示するコンポーネントの実装
integerLiteralDescriptions/index.jsの実装app/javascript/packs/integerLiteralDescriptions/index.jsimport Vue from "vue"; import Index from "../../components/integerLiteralDescriptions/Index.vue"; document.addEventListener("DOMContentLoaded", () => { const app = new Vue({ render: h => h(Index) }).$mount(); document.body.appendChild(app.$el); });コンポーネントの実装
app/javascript/components/integerLiteralDescriptions/Index.vue<template> <div> <header-view v-bind:title="title" v-bind:description="description"></header-view> <contents v-bind:contents="contents"></contents> </div> </template> <script> import HeaderView from "../HeaderView.vue"; import Contents from "./Contents.vue"; export default { components: { "header-view": HeaderView, contents: Contents }, data: function() { return { title: "title", description: "description", contents: [] }; } }; </script> <style scoped> </style>app/javascript/components/integerLiteralDescriptions/Contents.vue<template> <div> <table class="contents"> <tr> <th>名前</th> <th>英語訳</th> <th>表記例</th> <th>用途</th> </tr> <tr v-for="content in contents" v-bind:key="content.name"> <td>{{ content.name }}</td> <td>{{ content.english }}</td> <td>{{ content.sample }}</td> <td>{{ content.usage }}</td> </tr> </table> </div> </template> <style scoped> .contents { border: 1px solid gray; } .contents th, .contents td { border: 1px solid gray; } </style> <script> export default { props: { contents: Array } }; </script>再び
bin/webpack-dev-serverを実行し直し、 http://localhost:3000/integer_literal_descriptions/index にアクセスすると、下記画像のようなページが表示されるかと思います。これで API を叩いて取得したデータを受け取り、表示するためのコンポーネントが完成しました。
次は、先ほど作成したコンポーネントに、データを渡す処理を実装していこうと思います。
APIエンドポイントの実装
[rails-vue-app] rails g controller api/v1/integer_literal_descriptions index --no-helper --no-assets --no-view-specs create app/controllers/api/v1/integer_literal_descriptions_controller.rb route namespace :api do namespace :v1 do get 'integer_literal_descriptions/index' end end invoke erb create app/views/api/v1/integer_literal_descriptions create app/views/api/v1/integer_literal_descriptions/index.html.erb invoke test_unit create test/controllers/api/v1/integer_literal_descriptions_controller_test.rbコントローラーの実装
app/controllers/api/v1/integer_literal_descriptions_controller.rbclass Api::V1::IntegerLiteralDescriptionsController < ApplicationController def index + title = 'IntegerLiteralDescriptions#index' + description = '整数リテラルの分類表' + contents = get_integer_literals + result_values = { + title: title, + description: description, + contents: contents + } + render json: result_values + # https://jsprimer.net/basic/data-type/#integer-literal end -end + + private + + def get_integer_literals + [ + { + name: '10進数', + english: 'decimal', + sample: '42', + usage: '数値' + }, + { + name: '2進数', + english: 'binary digits', + sample: '0b0001', + usage: 'ビット演算など' + }, + { + name: '8進数', + english: 'octal', + sample: '0o777', + usage: 'ファイルのパーミッションなど' + }, + { + name: '16進数', + english: 'hexadecimal, hex', + sample: '0xEEFF', + usage: '文字のコードポイント、RGB値など' + } + ] + end +endアクセスの確認
この状態で、 http://localhost:3000/api/v1/integer_literal_descriptions/index にアクセスしてみると、下記のような画面が表示されるはずです。
一旦これで、 API のエンドポイントが完成しました。
コンポーネントから API を叩いてデータを取得
コンポーネントと API の Ajax 通信は axios というライブラリを使用して行います。4
まずはaxiosをインストールします。[rails-vue-app] yarn add --dev axiosinstall に成功していると、
package.jsonaxiosの項目が追記されているはずです。package.json"devDependencies": { + "axios": "^0.19.0", "webpack-dev-server": "^3.7.2" }次に
integerLiteralDescriptions/Index.vueを書き換えて、 axios でのデータ取得を実装します。app/javascript/components/integerLiteralDescriptions/Index.vueimport Contents from "./Contents.vue"; +import Axios from "axios"; export default { components: { "header-view": HeaderView, contents: Contents }, data: function() { return { title: "title", description: "description", contents: [] }; + }, + + created: function() { + this.updateContents(); + }, + + methods: { + updateContents() { + Axios.get("/api/v1/integer_literal_descriptions/index.json").then( + response => { + const responseData = response.data; + console.log(responseData); + } + ); + } }
createdで vue コンポーネントが作成されたタイミングでaxiosによるデータ取得を走らせるようにしています。
console.logで取得したデータを表示するようにしているので、 http://localhost:3000/integer_literal_descriptions/index を見てみましょう。
↓のようなログが出て、データ取得ができているはずです。
Tips: JS のデバッグ
ご存知な方も多いと思いますが、 JS では debugger を仕込むことで、デバッグ実行が可能になります。
debugger ステートメント | MDN具体的には下記のように仕込むことができます。
app/javascript/components/integerLiteralDescriptions/Index.vueAxios.get("/api/v1/integer_literal_descriptions/index.json").then( response => { const responseData = response.data; - console.log(responseData); + debugger; } );開発者ツールを開きながら、再度先ほどの http://localhost:3000/integer_literal_descriptions/index にアクセスしてみると、
debuggerを仕込んだ部分で処理が止まり、Consoleからその時点の各種データを覗くことができます。(↑画像の場合、画像下部でresponseDataの値を確認しています。)取得したデータをコンポーネント内で描画
先ほど
axiosで取得したデータを、画面に反映させます。
今回の場合、下記の部分を書き換えるだけです。app/javascript/components/integerLiteralDescriptions/Index.vueAxios.get("/api/v1/integer_literal_descriptions/index.json").then( response => { const responseData = response.data; - console.log(responseData); + this.title = responseData.title; + this.description = responseData.description; + this.contents = responseData.contents; } );http://localhost:3000/integer_literal_descriptions/index にアクセスしてみると、整数リテラル分類表が出てくるはず!
整数リテラル分類表
JS では 0 で始まる数値の直後に
b, o, xをつけると、それぞれ2進数、8進数、16進数が表現できるみたいですね。b, o, xは 英語訳を見てみると、それぞれ binary digits, octal, hex となっており、なるほどなーとなるんじゃないでしょうか。今回整数リテラル分類表をサンプルに組み込んだ理由は特にないです。気まぐれです。最後にちょろちょろ
せっかくなので、
http://localhost:3000/home/indexからhttp://localhost:3000/integer_literal_descriptions/indexに飛べるように、リンクボタンを追加しておきましょう。app/controllers/home_controller.rburl: 'https://github.com/t0yohei/rails-vue-app' } ], + inner_links: [{ + label: '整数リテラル分類表', + url: url_for(action: 'index', controller: 'integer_literal_descriptions') + }] }app/javascript/components/home/Contents.vue</td> </tr> </table> + <div v-for="inner_link in contents.inner_links" v-bind:key="inner_link.label"> + <button v-on:click="changeLocation(inner_link.url)" class="btn-push">{{ inner_link.label }}</button> + </div> </div> </template> <style scoped> @@ -23,6 +26,23 @@ .contents-table td { border: 1px solid gray; } +.btn-push { + margin: 10px; + max-width: 180px; + text-align: left; + background-color: rgb(24, 174, 238); + font-size: 14px; + color: #fff; + text-decoration: none; + font-weight: bold; + padding: 10px 24px; + border-radius: 4px; + border-bottom: 4px solid rgb(24, 174, 238); +} +.btn-push:active { + transform: translateY(4px); + border-bottom: none; +} </style> <script> export default { @@ -31,6 +51,11 @@ export default { type: Object, default: () => {} } + }, + methods: { + changeLocation(url) { + window.location.href = url; + } } }; </script>http://localhost:3000/home/index を開いて、こんな感じのボタンができていたら成功です。
最後に
今回のチュートリアルはここで終了です。 会社の人が Vue.js を触り始める時に使ってもらえたらなーと思いこのチュートリアルの作成を計画したのですが、せっかくなので Qiita に投稿してみることにしました。どこかのエンジニアの役に立つと幸いです。
今回の実装では CoffeeScript と turbolinks のセットアップを省いています。既存の rails プロジェクトで Vue.js を使用する場合は、 turbolinks と戦う必要がありそうです。 ↩
もし既存の rails プロジェクトに途中から導入する場合は、右記をご参考ください。rails/webpacker#installation ↩
- 投稿日:2019-07-07T21:08:40+09:00
【Laravel】Vue.jsでFile APIを使って画像プレビューを行なう方法
画像プレビューを実装
今回のゴールはこちら。
File API
File APIについて
ローカルで選択した File情報を取得できます。ただし、File APIを使わなくても以下の項目は普通に取得できます。
更新日時
ファイル名
データサイズ(バイト数)
MIMEタイプ(ファイルの種類)コード
sample.php<div id="imgview"> <img-view></img-view> </div>使用するVueをコンポーネントとして登録。
app.jsVue.component('img-upload', require('./components/ImgView.vue').default);ImgView.vue<template> <label for="file-sample"> <div class="drop"> <input class="input" id="file-sample" type="file" name="user_img" @change="onFileChange"> <i aria-hidden="true" class="fas fa-plus fa-7x"></i> <img class="img" id="file-preview" v-show="uploadedImage" :src="uploadedImage"> </div> </label> </template> <script> export default { data() { return { uploadedImage: "", }; }, methods: { onFileChange(e) { let files = e.target.files; this.createImage(files[0]); //File情報格納 }, //アップロードした画像を表示 createImage(file) { let reader = new FileReader(); //File API生成 reader.onload = (e) => { this.uploadedImage = e.target.result; }; reader.readAsDataURL(file); }, }, } </script>inputタグに
@changeを、imgタグにv-showを使います。
v-showに設定しているuploadedImageはimgタグのsrcにも設定されていて、uploadedImageにFile情報が入ると同時にsrcにもソースとしてFile情報が格納され表示されると言う仕組みです。なので最初の時点では何も入っておらず、v-showはfalseになるので
display: none;が適用されています。methods
onFileChangeでは選択されたFile情報を変数に格納。
files[0]を引数にcreateImage()メソッドを呼び出します。
ここでは、FileReaderときうオブジェクトを生成します。
onloadは正常にファイルを読み込んだときに発生するイベントです。
この中でuploadedImageに event.target.resultのデータを格納。
event.target.resultには画像データをテキストデータにしたものが入っています。
※これをData URIをスキーム化させると言うみたいです。最後にreadAsDataURL()で画像の読み込みを実行します。
- 投稿日:2019-07-07T17:38:30+09:00
Vue, Nuxtで使いそうな CSS フレームワークをまとめてみた
はじめに
今後、新しいプロジェクトで、Vue,Nuxtを使いそうなので、調べたところ今時のフロントエンドはいい感じのライブラリーがたくさんあることを知りました。
どれが、どのようにいいとかが正直わからかったので、とりあえず公式サイトまとめを作ってみました。対象読者
- Vue(Nuxt)を始めるよって人
- フレームワークたくさんあるので、ドキュメントをまとめたものをみたいって人
フレームワークたち
Boostrap
言わずと知れた。
その昔、サイトのデザインを見ればこれはBootsrapでできているってわかったなぁ。Bulma
Flex Box レスポンシブ対応
公式サイトTachyins
軽量cssフレームワーク
公式サイト参考記事
手軽にコーディングするならやっぱりBootstrapよりTACHYONSだなぁElement UI
デスクトップ用のフレームワーク(レスポンシブではない)
iView
中華製のフレームワーク
Element UI と似ている
公式サイト参考記事
Vue.jsのUIデザインフレームワークにiViewを導入する
Who's using iViewvuetify
日本語記事が多い印象
公式サイト
参考記事
Vueでもっと幸せになりたいあなたへ。VueのUIコンポーネントライブラリVuetifyのススメBuefy
Bulma をベースにした、vue用のフレームワークらしい
Bulmaと何が違うのかな?軽量なのかな?
公式サイトAnt desing
管理画面用のフレームワーク
https://ant.design/docs/spec/introduceTailwind CSS
UIをコンポーネントで提供するのではなく、classで提供している
いい感じのを作って欲しいという、ふんわりとした要件に使いやすいかも参考サイト
Nuxt.js + Tailwind CSS で爆速コーポレートサイト作成終わりに
パッとみ、簡素な記事になってしまった。。。
もっとUIを考えたいと思った記事でした。
- 投稿日:2019-07-07T15:55:26+09:00
Nuxtでaxiosのエラーハンドリングをグローバルに行う
Nuxtを書いていて、401、500エラーのような対応処理が同じエラーに対しては、プラグインファイルの中で一括で行いたかったので、その方法を調べました。
onErrorヘルパーを使う
例えば認証にJWTを使ってajax通信を行う際、プラグインファイルを作って、interceptor的にresponseヘッダにjwtトークンを埋め込む処理を書くと思います。こんな感じ。
plugins/axios.jsexport default ({ $axios }) => { $axios.onRequest(config => { const accountToken = localStorage.getItem('accountToken') if (accountToken) { config.headers.common['x-access-token'] = accountToken } }) }エラーハンドリングをグローバルに行いたい場合、onErrorヘルパーを使います。
// $axios.onRequest(){...} $axios.onError(error => { if (error.response.status === 401) { // 401エラーの場合の処理を書く } })他にもヘルパーはいくつかあるので、気になる人はaxios公式ドキュメントを読みましょう。
Axios Modules404エラーのハンドリング
404エラーのハンドリングはaxios側ではできません。(ajax通信が関係ないので当たり前ですが...)
404エラーに関してはrouterに任せましょう。Nuxtの場合は404エラーのハンドリングはnuxt.config.jsに記述できます。
router: { extendRoutes(routes, resolve) { routes.push({ path: '*', component: resolve(__dirname, 'pages/404.vue') }) } }こちらの記述は、「Nuxt.jsで存在しないURLにアクセスされた時に404ページ(任意のページ)を表示する方法」を参考にさせてもらいました。
これにより、ルーティングに存在しないURLにアクセスされた場合に/404に遷移させることができます。あとは404.vueをpages/配下に置けばOKです。
まとめ
Nuxtはこういった処理が簡単にできるので便利ですね。
- 投稿日:2019-07-07T15:22:50+09:00
ふとしたキッカケで Svelte を使ってみた
最近、ふとしたキッカケで JSフレームワークである Svelte を使ってみたので簡単に感想をメモっておく。
結論
結論だけいうと、コンパイラなので依存が実質的に何もないし、実際に軽くて早くてコード量が少ないのは事実なので、使い慣れてきたらかなり良さそう。
ただし複数人でコードをいじる場合には、フレームワーク自体のルールがほとんどないので、チーム内での統一ルールなどが無いと扱いにくいかも、特に store 周り
Svelteの特徴
- JSコンパイラで Vanilla JS としてコンパイル結果を出力する
- 海外ではそれなりに知名度がある
- vuejs に影響を受けているので書き方や概念が近い
- 軽量かつ高速
- ボイラーブレートコードを忌み嫌っている
- コード量を出来るだけ少なく済むようにしている
- SSRが可能だが試してはいない
- Legacyモードでコンパイルすることにより IE9 対応可能
- store の state を直接バインディングできる
- テンプレート側で Promise の状態を見て分岐ができる
こんな感じ{#await promise} <p>pendingだよ</p> {:then hoge} <p>resolveした</p> {:catch} <p>rejectした</p> {/await}私見
- Riot をブラッシュアップした感じ
- コンポーネントは vuejs の SFC と感覚としては同様
- 軽量かつ高速なのは体感できるレベルでわかる
- store はかなり使い勝手が違う
- state と props に相当するものがあるが、React や vuejs とは違うのであんまり気にしない方がいい
- React や vuejs ですら「重い」という環境で採用されている模様
- router はないのだが、sapper という svelte を利用したフレームワークの方にはついてる
メリット
- 依存が無いので環境の立ち上げが楽
- コード量は少ない
- store がビルトインされている
- 学習コストは低い
- コンパイラなのである程度のエラーは事前に検知できる
- 本体リポジトリは議論や更新が活発
デメリット
- 日本語のドキュメントがない
- 日本のユーザーグループなどもない
- したがって講習会や勉強会などもない
- というか日本語の情報は基本的に無い
- react や vuejs のような便利な devtool がない
よくわかっていない点
- store の set/update で state がまるごと更新される(詳細は下記)
- vuex の getter にあたる deriverd というものがある
- store の deriverd 内では非同期処理は扱えない(多分)
store.js/* store はこんな感じ */ export const hogeStore = writable(0); 参照するときは $hogeStore /* カスタムするとこんな感じ */ const router = () => { const { subscribe, set } = writable('index') return { subscribe, index: () => set('index'), detail: () => set('detail'), } } $router で index が返ってくる、router.detail() で更新、async でも書ける /* 複数の値を持ちたい場合 */ export const hogeStore = writable({ hoge: '', fuga: '' }); $hogeStore.hoge で参照 $hogeStore.hoge = 'hogehoge' で普通に更新される hogeStore.set とかすると $hogeStore が丸ごと更新される hogeStore.hoge.set() とかそういうことはできないっぽい
















