- 投稿日:2020-03-21T23:51:10+09:00
[Rails+Vue.js]に係るCRUD入門〜Part Last: Docker化編〜
Rails+Vue.js+Webpackerによる「Create, Read, Update, Destroy」処理のチュートリアルを記述する...
しかし、半年前にあらかた学びたいことは終わり、業務では Nuxt を見ることが多くなるため、シリーズとして終了とする。<概要>
□ 本記事の内容
- 開発環境を Docker 化する。
- 合わせて Ruby と Rails のバージョンを上げる。
- 今回のコードは,GitHubのコミット履歴で確認可能である。
□ 記事共通
- 目次
実装機能
- お気に入りの本に係る登録,参照,編集,削除処理
- 非同期通信(Ajax)による[Rails+Vue.js]のCRUD処理
- Single Page Application(SPA)
- ユーザー登録機能(JWTによるトークン認証)
- 開発環境をDocker化
開発環境
- MacOS Mojave
- Ruby(2.5.1 => 2.7.0)
- Ruby on Rails(5.2.1 => 6.0.1)
- Vue.js(2.6.10)
- Yarn(1.17.0)
- Webpack(4.39.2)
学習情報URL
<本文>
□ 各種ファイルの調整
■ Ruby, Rails のバージョンアップ関係
application.rb... - config.load_defaults 5.2 + config.load_defaults 6.0 + config.time_zone = "Asia/Tokyo" ...Gemfile... - ruby '2.5.1' + ruby '2.7.0' ... - gem 'rails', '~> 5.2.3' + gem 'rails', '6.0.2' ... - gem 'sqlite3' + gem 'mysql2'Gemfile.lock# 全削除
■ Docker化: サーバ起動設定
bin/server#!/bin/ash -i rm -f /app/tmp/pids/server.pid bundle exec foreman start -f Procfile.devProcfile.devweb: bundle exec rails s -p 5000 -b 0.0.0.0 webpacker: ./bin/webpack-dev-server --host 0.0.0.0 --port 3035config/webpakcer.yml... dev_server: https: false - host: localhost + host: 0.0.0.0 port: 3035 - public: localhost:3035 + public: 0.0.0.0:3035 ...■ Docker化: mysql の接続設定
config/database.ymldefault: &default adapter: mysql2 charset: utf8mb4 collation: utf8mb4_bin encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> host: <%= ENV.fetch("APP_DATABASE_HOST") { '127.0.0.1' } %> port: <%= ENV.fetch("APP_DATABASE_PORT") { '3306' } %> username: <%= ENV.fetch("APP_DATABASE_USERNAME") { 'root' } %> password: <%= ENV.fetch("APP_DATABASE_PASSWORD") { 'pass' } %> development: <<: *default database: rails_vue_bookshelf_development test: <<: *default database: rails_vue_bookshelf_test production: <<: *default database: rails_vue_bookshelf_production■ Docker化: redis の接続
config/initializers/redis.rb# docker-compose.yml で定義する `REDIS_URL: redis://redis:6379`が入る。 REDIS ||= Redis.new(url: ENV['REDIS_URL'] || 'redis://localhost:6379')books_controller.rb... # 'json'の箇所を全てシンボルに置換する。 # Rails6へのバージョンアップによりシンボルしか受け付けなくなったみたい。 - render 'index', formats: 'json', handlers: 'jbuilder' + render 'index', formats: :json, handlers: 'jbuilder' ... - render 'show', formats: 'json', handlers: 'jbuilder' + render 'show', formats: :json, handlers: 'jbuilder' ... - render 'index', formats: 'json', handlers: 'jbuilder' + render 'index', formats: :json, handlers: 'jbuilder' ...□ Docker 化の実行
■ Dockerfile の準備
bash$ mkdir docker $ touch docker/DockerfileDockerfileFROM ruby:2.7.0-alpine3.10 ENV LANG C.UTF-8 ENV TZ Asia/Tokyo ENV BASE_PACKAGES="alpine-sdk build-base tzdata" \ WEBPACKER_PACKAGES="python2 yarn nodejs-current nodejs-npm" \ BUILD_PACKAGE="mysql-client mysql-dev" \ FAVORITE_PACKAGE="less" RUN apk update && \ apk upgrade && \ apk --update --no-cache add \ ${BASE_PACKAGES} \ ${WEBPACKER_PACKAGES} \ ${BUILD_PACKAGE} \ ${FAVORITE_PACKAGE} WORKDIR /app COPY Gemfile \ Gemfile.lock \ package.json \ yarn.lock \ /app/ RUN bundle install --jobs=4 RUN yarn install # https://github.com/bundler/bundler/issues/6154 ENV BUNDLE_GEMFILE='/app/Gemfile'■ docker-compose.yml の準備
bash$ touch docker-compose.ymldocker-compose.ymlversion: '3' services: datastore: image: busybox volumes: - db_data:/var/lib/mysql - redis_data:/data - bundle_install:/usr/local/bundle - node_modules:/app/node_modules db: image: mysql:5.7 ports: - 3306:3306 volumes: - db_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: pass app: build: context: . dockerfile: ./docker/Dockerfile command: ["bin/server"] ports: - 5000:5000 - 3035:3035 volumes: - bundle_install:/usr/local/bundle - node_modules:/app/node_modules - ./:/app:cached environment: APP_DATABASE_HOST: db APP_DATABASE_USERNAME: root APP_DATABASE_PASSWORD: pass REDIS_URL: redis://redis:6379 depends_on: - db - redis stdin_open: true tty: true redis: image: redis ports: - "6379:6379" volumes: - redis_data:/data stdin_open: true volumes: db_data: redis_data: bundle_install: node_modules:■ その他設定
- ご覧通りデータベースの初期設定に必要なコード群です。
$ docker/bootstrap
で一括で必要なコマンドを実行してもらうことが目的です。bash$ touch docker/bootstrap $ chmod 777 docker/bootstrap#!/usr/bin/env bash docker-compose run --rm app ./bin/rails db:create docker-compose run --rm app ./bin/rails db:migrate docker-compose run --rm app ./bin/rails db:seed■ コンテナの構築及び起動
- 以下を実行して http://localhost:5000 で起動を確認したら成功です。
bash$ ./docker/bootstrap $ docker-compose build $ docker-compose up -d〜Part Last: Docker化編終了〜
- 投稿日:2020-03-21T22:56:08+09:00
Class Component + TypeScript の Vue アプリケーションを Composition API で作り直したので思ったことを書く
はじめに
少し前に作って、更新が滞っていたアプリケーションに機能を追加したかった。
ちょうどよかったので Vue + Class Decorator + TypeScript の構成だったが、 Vue + CompositionAPI + TypeScript に作り直した。
この記事では、その過程で思ったことや考えたことを共有したい。作成しているアプリケーションは以下。
- ソース: https://github.com/sterashima78/vue-webpage-builder
- アプリケーション: https://sterashima78.github.io/vue-webpage-builder
- 使い方はソースについている README を見てください
作り直す前
- コンポーネント定義はクラススタイル
- vue-class-componentでやってた
- Vuex もクラススタイル
- vuex-module-decoratorsでやってた
とりあえず型がついていたのでよかった。
作り直すにあたって考えたこと
Vuexいる?
なんで Vuex を使いたいのかを考えた。
- 共有したい状態を中央管理したい
- composition-api でできる
- タイムトラベルデバッグがしたい
- 個人的にはそんなに使わない
- コンポーネントに注入できる
- vuex-module-decorators 使ってたら結局 store は注入しない
- コンポーネントの注入された共有された状態を無秩序に触れると困ることが多いのでむしろ明示的にインポートするほうがいい。
Vuex いらない。
composition-api を使って状態を共有する方法はいくつかの記事があるけどかんたんに紹介する。
export const useState = ()=> { return { state: reactive({ count: 0 }) } }上記の場合は useState がコールされるたびに新しく状態が生成されるので、コンポーネント間で独立したものになる。
const state = reactive({ count: 0 }) export const useState = ()=> ({ state })上記の場合は useState がコールされると state が返却されるが、この state はモジュール内で定義されたものなので、コンポーネント間で共有される。
状態を変更するためのインターフェースを提供したければ普通に状態を変更する関数を公開すればいい。
const _state = reactive({ count: 0 }) // 公開する状態はreadonly const state = computed(()=> _state) // 注入された状態を更新する export const increment = (state) => () => state.count++ export const decrement = (state) => () => state.count-- export const useState = ()=> ({ state, increment: increment(_state), decrement: decrement(_state) })場合にもよるが、状態を変更する関数は上記のように変更対象の状態を注入できるようにしておくと試験がしやすい。
import { increment } from "./composition" describe("useState", ()=> { describe("increment", ()=> { test("count を 1 増やす", ()=> { const mockState = { count: 100 } increment(mockState)() expect(mockState.count).toBe(101) }) }) })これだとあんまり効果が実感できないが、例えば 1000 以上は増えちゃいけないみたいな要件があるときには試験しやすい。
Class Component と Vue の相性悪くない?
基本的にコンポーネントはコンポーネントが持っている状態を、ユーザからのアクションに応じて変更する。そして、この状態に応じてUIが更新される。
ここから考えると、自分の状態とそれを変更するロジックをひとまとめにする class をコンポーネント定義に使うのはとても自然に感じてた。
実際 Class Component は this の型付けもうまくできていたしはじめは使いやすかった。
ただ、これを複数のクラスやコンポーネントとの連携を考える急に難しくなると感じた。OOPではオブジェクト間でのメッセージングでソフトウェアを組み立てていくけど、実際はコンポーネント定義の糖衣構文として使っているだけなので、そういうメッセージングを行う頭で考えてしまうと設計を間違える。
まぁ、これ自体はそういう頭で考えなければいいのだが、コンポーネントを定義するクラスに他のクラスに依存させたいときはまた難しい。
OOPでこれを行うときはコンストラクタインジェクションなどで依存を注入するなどがポピュラーだと思うが、 Vue のコンポーネント定義ではコンストラクタ定義ができない。
そのため、直接クラス内でインスタンスを生成する必要がある。こんなふうにしたいけどできない。class HogeComponent extends Vue { constructor(private domain: DomainClass) { } hoge() { this.state = this.domain.foo("bar") } }そのために provide, inject の仕組みを Vue は用意してくれているが、コンポーネントからコンポーネントへの提供を想定しているので試験などやりにくい。
class コンポーネント使いにくい。
composition-api を使ってあげるとこの辺がうまく解決できる。
重要なのは、 setup メソッドは 所謂 main に相当するものであると考えることだと思う。
つまり、 setup にはロジックを記述せずに依存インポートと、composition 関数への依存注入に責任をもたせる。composition// 依存はダイレクトにモジュールをインポートしないで引数で注入する export useHoge = (domainLogic)=> { const state = ref(null) const hoge ()=> { state.value = domainLogic("bar") } return { state, hoge } }component<script> import { domainLogic } from "@/domain" import { useHoge } from "./composiotons/" export default defineComponent({ setup() { // 依存の解決だけ行う return { ...useHoge(domainLogic) } } }) </script>こうすることで composition 単体での試験がとてもやりやすくなる。
describe("useHoge", ()=> { describe("hoge", ()=> { const mockFn = jest.fn() mockFn.mockImplementation(()=> "foo") const { hoge, state } = useHoge(mockFn) hoge() expect(state).toBe("foo") expect(mockFn.mock.calls[0][0]).toBe("bar") }) })おわりに
Class Componet のスタイルから、 Composition API のスタイルに書き換えたときに考えたことを記載した。
基本的に試験の容易性を高めることが、結果的に拡張性・保守性を高めることにつながると考えているので、やや偏っていたかもしれないが参考になれば嬉しい。これ以外に composition-api を使ったプロジェクトでのテストについて以下の記事を作成しているので、よければそちらも参考にしてほしい。
https://qiita.com/sterashima78/items/b72c722a3043dcfd99bd
また、関心を分離しながらプログラムを書くために、プレーンなJSから composition-api を使うまでのステップアップしていくさまを以下で紹介している。
https://qiita.com/sterashima78/items/e5518dabbfccdf6a205dcomposition-api は適切に利用すれば Vue の抱えていた多くの問題をすると思うので、ぜひいろんな人に使い込まれてベストプラクティスが構築されていってほしいと思う。
- 投稿日:2020-03-21T22:20:51+09:00
【JS/Vue】VueXの浅い理解
注意
VueXについては、下記の記事を読むのが一番です。
vuexをまだ理解していない全人類に捧ぐvuexを利用したコードの図解 - QiitaVueX
VueXの構成
- Actions
- Mutations
- State
以下全体図
全体の流れ
- stateを確認
- actionを呼び出し
- mutationsへcommit
- stateを更新
this.$store.dispatch('xxx') でコンポーネント内でアクションをディスパッチできます。あるいはコンポーネントのメソッドを store.dispatch にマッピングする mapActions ヘルパーを使うこともできます(ルートの store の注入が必要です):
アクション | Vuexより引用store
アプリケーションの 状態(state) を保持するコンテナです。
storeの状態を変更する際は、Mutationsにコミットをすることで store を変更することが可能です。stateの中にstoreが存在しています。
templateから$storeにアクセスすることができます。
$sotre.state
actionの呼びだし方法
dipatch
this.$store.dispatch('メソッド名')mapActions
methods: { ...mapActions(['login']) }actionで定義されるcommit
actionの定義をする際、下記のようにmutationsへcommitをします。
その際のcommitが少しとっつきづらいです。// 実装 actions: { toggleSideMenu ({ commit }) { commit('toggleSideMenu') } } // 本当は actions: { toggleSideMenu (context) { context.commit('toggleSideMenu') } }上記で記載の通り、
context.commit
を呼び出しています。
詳しい解説は下記がわかりやすかったです。
vuexで登場する分割代入の説明 - Qiita
分割代入 - JavaScript | MDNFlux
VueXは、Fluxがベースになっています。
Fluxを実装したのが、Reduxで、そこからVueXが生まれました。
flux/examples/flux-concepts at master · facebook/flux · GitHubより引用全ての状態を持つべきか
公式ドキュメントでも述べられている通り、全てを置かなくても良いみたいです。
Vuex を使うということは、全ての状態を Vuex の中に置くべき、というわけではありません
ステート | Vuexより引用参考記事
VueとVuexの間の値の連携の仕方 - Qiita
2018年Vue.jsとVuexを使ってる人には必ず知っていてほしい開発やメンテナンスの際に役立つ設計とTipsとサンプルコード - Qiita
- 投稿日:2020-03-21T21:23:18+09:00
【JS/Vue】ライフサイクル
主に下記のUdemyのコースをベースに書きました。
Vue.js + Firebaseで作るシングルページアプリケーション | UdemyVueライフサイクル
ドキュメントに書かれているライフサイクルの図は下記の通りです。
大まかに定義すると,下記の流れのようになっており、それぞれに作成前、後が存在しています。
- Vueインタンス作成
- マウント
- DOM アップデート
- Vueインタンス削除
またこのライフサイクルでは、ライフサイクルフックで処理を追加できるようになっています。
ライフサイクルフックとは、上図で書かれてるbeforeCreate
、created
などの赤枠で囲われているものをさします。
この中で色々実装することで、細かい挙動の変更を行うことができます。Vueインタンス作成
beforeCreate
、created
がインスタンスの作成に該当します。
下記のような実装でインスタンスを作成することができます。new Vue({ router, store, render: h => h(App) }).$mount('#app')マウント
beforeMount
、mounted
が該当します。
定義については下記がわかりやすかったです。既存のDOM要素をVue.jsが生成するDOM要素で置き換えること。
Vue.js入門 2章 Vue.jsの入門(コンストラクタ、マウント、data) - Qiitaより引用仮想DOM
beforeUpdate
、updated
が該当します。
つまり変更差分のところだけ、アップデートしようという考え方です。なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiita
仮想DOMは本当に“速い”のか? DOM操作の新しい考え方を、フレームワークを実装して理解しよう - エンジニアHub|若手Webエンジニアのキャリアを考える!実験
下記のように html, js を定義して、 console上でメッセージを書き換えると、
beforeUpdate
、updated
が動いていることがわかります。<div id="app"> {{ message }} </div>const vm = new Vue({ el: '#app', data() { return { message: 'こんにちは' } }, beforeUpdate() { console.log('再描画前') }, updated() { console.log('再描画後') } }) window.vm = vm便利なサイト
下記のサイトで色々 js の実装とかを試せます。
めちゃ便利です。
JSFiddle - Code Playgroundその他参考にした記事
- 投稿日:2020-03-21T19:09:55+09:00
vue cli: lazy loadって?やっておいたほうがいい理由&やり方を解説
備忘録です。
vue cliでは、vue-routerを使うことで、いとも簡単にpathがつくれます。
つまりabout, contact, infoなど、プロジェクト内にいくつかページを設定したいときに、blog.jp/about
blog.jp/contact
blog.jp/infoとそれぞれのリンクをつくれるようになります。
vue cliでプロジェクトを作成するときにvue-routerを含むよう設定しておけば、routerというフォルダが自動的に作成され、そのなかにあるindex.jsからrouterの作成・編集が行えるようになります。この記事はvue-routerについてではないので、細かいところは割愛します。
(vue-routerのドキュメントは、ここから確認できます)
vue-routerで新たなpathをつくる際にlazy loadを設定しておかないと、ウェブサイトを開いたときにウェブサイト全体で使われる全てのJSが一斉に読み込まれてしまいます。
なぜlazy loadするべきか
一見、「別にいいじゃん」と思うかもしれません。ところがページが重い場合、「ページに必要となるJSを全部ロードしてからページを開く」なんてことをしていると、ページの動作が鈍くなりかねません。
lazy loadであれば、ユーザーがページにクリックした際に、そのページのJSファイルがロードされるよう設定ができます。なので、サイトにアクセスした瞬間から重すぎてページがロードしない、なんて事態を防げます。そうすることで、ユーザーはページ内をスイスイ回遊することができます。
方法
たとえばAboutというページがあるとします。
まずはlazy loadをしなかった場合のコードを見てみましょう。
About.vueは、通常通りimport About from〜
というコードでimportされています。import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../views/Home.vue"; import About from "../views/About.vue";//ここでAbout.vueをインポート Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home }, { path: "/about", //パスをつくる name: "About", //vueファイル内でアクセスできる名前をつける } ];一方でlazy loadをした場合は、
routes
のなかにあるcomponent
というプロパティを通して、About.vueをインポートします。import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../views/Home.vue";//ここでAbout.vueをインポートしない Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home }, { path: "/about", //パスをつくる name: "About", //vueファイル内でアクセスできる名前をつける component: () => import(/* webpackChunkName: "about" */ "../views/About.vue") // ここでlazy loadをしている! } ];
component
を定義することで、ページが呼び出されたときにだけ、About.vueが読み込まれるようになります。ちなみに
/* webpackChunkName: "about" */
これはなんぞや?と思うかもしれません。lazy loadのコードを書いたあと、
Chrome Developer Tools?network
を開き、Aboutのページをクリックしてみると、app.jsとは別に0.js
というファイルがロードされていることが確認できるかと思います。つまりwebpackChunkName
を設定しないと、デフォルトとして数字でファイル名が登録されてしまうのです。
about.js
とわかりやすく表示できるよう、/* webpackChunkName: "about" */
を付け加えておきましょう。vue.jsの公式ドキュメントにも記載されています。
以上です?
- 投稿日:2020-03-21T18:11:00+09:00
Vue.js 自分用
テンプレート構文
methodsの使い方
<div id="app"> <p v-once>{{ message }}</p> <!-- v-onceを使用 dataプロパティからmessageのValueを呼び込む --> <p>{{ sayHi() }}</p> <!-- 関数を呼ぶ --> <p>{{ message }}</p> <!-- この記述が最適 --> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ message: 'Hello World', }, methods:{ sayHi: function(){ return this.message = 'Hello Vue.js' //thisを忘れない } } }); </script>dataのバインディング v-bind
<div id="app"> <a v-bind="twitterObject">Twitter</a> <!-- v-bind --> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ twitterObject:{ href: 'https://twitter.com', id:3 }, }, }); </script>countUp v-on
<div id="app"> <p>現在のクリック回数は{{ number }}です。</p> <!-- dataのnumberの数を取得 --> <!-- v-on:click --> <button v-on:click="countUp(2)">カウントアップ</button> <!-- countUp関数に引数(2)を --> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ number: 0, }, methods:{ countUp: function(times){ //times 引数の2 this.number += 1 * times // this } } }); </script>countUp 省略記法
<button @click="countUp">カウントアップ</button> <!-- @記法で v-on:を省略 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ number: 0, //初期値の設定 }, methods:{ countUp: function(){ this.number += 1; //this 関数内 } } }); </script>mouseover
<div id="app"> <p v-on:mousemove="changeMousePosition(1, $event)">マウスオーバ- <!-- 第一引数に値を、第二引数にイベント --> <span v-on:mousemove.stop>反応しないでください~~</span></p> <!-- stopをつけると反応しない --> <p>りんご:{{ x }}個, みかん:{{ y }}個</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ x: 0, //初期値 y: 0, //初期値 }, methods:{ changeMousePosition: function(divideNumber, $event){ this.x = event.clientX + divideNumber; this.y = event.clientX + divideNumber; } } }); </script>アラート機能
<div id="app"> <input type="text" v-on:keyup.enter="myAlert"> <!-- イベントkeyupに.enter --> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', methods:{ myAlert: function(){ // methods内に 関数宣言 alert('アラート'); } } }); </script>非同期処理 v-model
form の input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成するには、v-model ディレクティブを使用することができます。
<div id="app"> <input type="text" v-model="message"> <!-- v-model --> <h1>{{ message }}</h1> <!-- data要素 --> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ message: 'こんにちは', }, methods:{ countUp: function(){ this.number += 1; } } }); </script>自動更新 watch (setTimeout js)
時間とともに値が0に変換。
<div id="app"> <p>{{ counter }}</p> <button @click="counter += 1">+1</button> <p>{{ lessThanThree }}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ counter:0, //初期値の設定 }, computed: { //computedプロパティの生成 lessThanThree: function(){ return this.counter > 3 ? '3以上' : '3以下'; } }, watch:{ // watch特定のデータが変わった時に処理を実行したいものを記述 counter: function(){ //非同期の時によく使用 let vm = this; setTimeout(function(){ vm.counter = 0 }, 3000) } } }); </script>classのバインディング
クラスオブジェクトを適用 buttonによって色を変更する
<style> .red{ color: red; } .bg-blue{ background-color: blue; } </style> <div id="app"> <button @click="isActive = !isActive">切り替えボタン</button> <h1 :class="classObject">Hello2</h1> <!-- ! オブジェクト形式で書く--> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ isActive:true, //boolean型 }, computed:{ classObject: function(){ return{ //クラスオブジェクト関数が呼ばれた時の返り値を記載 red: this.isActive, 'bg-blue': !this.isActive } } } }); </script>配列で指定
<style> .red{ color: red; } .bg-blue{ background-color: blue; } </style> <div id="app"> <h1 :class="[color, bg]">Hello3</h1> <!-- ! 配列で指定する--> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ color: 'red', bg: 'bg-blue', }, }); </script>配列で指定 複数のオブジェクト
<body> <div id="app"> <h1 :style="[styleObject, baseStyle]">Hello World</h1> <!-- ! オブジェクトを複数当てる時--> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: '#app', data:{ styleObject:{ //オブジェクト生成 color: 'red', 'background-color': 'blue', }, baseStyle:{ //オブジェクト生成 fontSize: '60px' } }, }); </script>
- 投稿日:2020-03-21T16:58:06+09:00
Mac + Vue + Vue Test Utils + Vuetify の開発環境構築
nodebrew インストール
$ brew install nodebrew $ nodebrew help|head -n1 nodebrew 1.0.1 $ nodebrew setup Fetching nodebrew... Installed nodebrew in $HOME/.nodebrew ======================================== Export a path to nodebrew: export PATH=$HOME/.nodebrew/current/bin:$PATH ======================================== $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile $ source ~/.bash_profile $ nodebrew install-binary stable $ nodebrew use stable $ node -v v12.4.0 $ nodebrew use v10.16.0 $ node -v v10.16.0 $ npm update -g npm $ npm -v 6.14.3yarn インストール
$ brew install yarn $ yarn --version 1.17.0@vue/cli インストール
$ yarn global add @vue/cli $ yarn global upgrade --latest @vue/cli @vue/cli 4.2.3 $ yarn info vue | grep version: version: '2.6.11',Vue インストール
$ mkdir -p ~/Developer/sample-vue && cd ~/Developer/sample-vue $ vue create sample-app $ yarn serveVue インストール後のツリー
~/Developer/sample-vue/sample-app ├── README.md ├── babel.config.js ├── node_modules ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ └── main.js └── yarn.lock $ yarn serveVue Test Utils インストール
$ cd ~/Developer/sample-vue/sample-app $ vue add --dev @vue/test-utils @vue/cli-plugin-unit-jestVue Test Utils インストール後のツリー
~/Developer/sample-vue/sample-app ├── README.md ├── babel.config.js ├── jest.config.js ■ 追加 ├── node_modules ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ └── main.js ├── tests ■ 追加 │ └── unit ■ 追加 │ └── example.spec.js ■ 追加 └── yarn.lockTEST 実行結果
$ cd ~/Developer/sample-vue/sample-app $ yarn test:unit tests/unit/example.spec.js yarn run v1.22.4 $ vue-cli-service test:unit tests/unit/example.spec.js PASS tests/unit/example.spec.js HelloWorld.vue ✓ renders props.msg when passed (26ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.172s Ran all test suites matching /tests\/unit\/example.spec.js/i. ✨ Done in 3.99s.Vuetify インストール
$ cd ~/Developer/sample-vue/sample-app $ vue add vuetify $ yarn info vuetify | grep version: version: '2.2.18', $ yarn serve~/Developer/sample-vue/sample-app ├── README.md ├── babel.config.js ├── jest.config.js ├── node_modules ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ ├── logo.png │ │ └── logo.svg ■ 追加 │ ├── components │ │ └── HelloWorld.vue │ ├── main.js │ └── plugins ■ 追加 │ └── vuetify.js ■ 追加 ├── tests │ └── unit │ └── example.spec.js ├── vue.config.js └── yarn.lockTEST
vi ~/Developer/sample-vue/sample-app/src/components/HelloWorld.vue~/Developer/sample-vue/sample-app/src/components/HelloWorld.vue<template> <div> <p id="htmlMessage">{{ message }}</p> <v-btn @click="onClick" small>CountUp</v-btn> <p id="count">{{ count }}</p> </div> </template> <script> export default { data: () => ({ message: 'HelloWorld', count: 0 }), methods: { onClick () { this.count++ } } } </script>~/Developer/sample-vue/sample-app/tests/unit/example.spec.js~/Developer/sample-vue/sample-app/tests/unit/example.spec.jsimport Vue from 'vue' import Vuetify from 'vuetify' import { createLocalVue, mount, shallowMount } from '@vue/test-utils' import HelloWorld from '@/components/HelloWorld.vue' Vue.use(Vuetify) const localVue = createLocalVue() describe('CustomCard.vue', () => { let vuetify let wrapper beforeEach(() => { vuetify = new Vuetify() wrapper = mount(HelloWorld, { localVue, vuetify, }) }) it('renders a vue instance', () => { expect(wrapper.isVueInstance()).toBe(true); }) it('Checks the message of vm', () => { let vmMessage = wrapper.vm.message expect(vmMessage).toMatch('HelloWorld') }) it('Checks the message of html', () => { let htmlMessage = wrapper.find('#htmlMessage').text() console.log(htmlMessage) expect(htmlMessage).toMatch('HelloWorld') }) })ユニットテスト
$ cd ~/Developer/sample-vue/sample-app $ yarn test:unit tests/unit/example.spec.js yarn run v1.22.4 $ vue-cli-service test:unit tests/unit/example.spec.js PASS tests/unit/example.spec.js CustomCard.vue ✓ renders a vue instance (81ms) ✓ Checks the message of vm (27ms) ✓ Checks the message of html (31ms) console.log tests/unit/example.spec.js:32 HelloWorld Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total Snapshots: 0 total Time: 1.531s, estimated 2s Ran all test suites matching /tests\/unit\/example.spec.js/i. ✨ Done in 2.79s.Vueでunitテストをする為の基礎基礎メモ
https://www.nogson.blog/entry/2019/01/30/175723
関数名 説明 利用ケース mount 子コンポーネントに実際のコンポーネントを使う コンポーネント同士で連携が必要な場合 shallowMount 子コンポーネントにダミーのコンポーネントを使う コンポーネント単体でのテスト
Matcher 説明 使用例 .toBe(value) resultとactual等価であることの比較 expect(4).toBe(4) .toEqual(value) resultとactualの連想配列の中身が一致するかの比較 expect({two: 2,one: 1}).toEqual({one: 1,two: 2}) .toMatch(regexp) resultに対して正規表現で比較 expect('abcdefg').toMatch(/abc/) .toBeGreaterThan(number) resultがactualより大きい expect(11).toBeGreaterThan(10) .toBeGreaterThanOrEqual(number) resultがactual以上 expect(10).toBeGreaterThanOrEqual(10) .toBeLessThan(number) resultがactualより小さい expect(9).toBeLessThan(10) .toBeLessThanOrEqual(number) resultがactual以下 expect(10).toBeLessThanOrEqual(10) .toBeCloseTo(number,桁数) resultの特定の少数桁までactualと一致するかを比較(jsは小数点計算で誤差がでる為) expect(0.1 + 0.2).toBeCloseTo(0.35, 1) .toHaveLength(number) resultのlengthとactualと一致するかを比較 expect([1,2]).toHaveLength(2) .toBeInstanceOf(class) resultがactualと同じclassのインスタンス化か比較 class Klass {} expect(new Klass()).toBeInstanceOf(Klass) .toHaveProperty(value) resultの特定のkeyと一致するか expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a') // key aがあるか expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a',false) // key aはfalseか expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('b.c',1) .toMatchObject(value) resultの一部とactualが一致するか expect({a:1,b:2}).toMatchObject({a: 1}) .toContain(value) resultにactualが入っているか expect([1,2]).toContain(2) expect('ABCDEFG').toContain('EFG') .toBeNull() resultがNullかどうか expect(null).toBeNull() .toBeUndefined() resultがUndefinedかどうか expect().toBeUndefined() 参考
インストール — Vue.js
https://jp.vuejs.org/v2/guide/installation.htmlクイックスタート — Vuetify.js
https://vuetifyjs.com/ja/getting-started/quick-start/Guides | Vue Test Utils
https://vue-test-utils.vuejs.org/guides/#getting-started@vue/cli-plugin-unit-jest
https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jestExpect · Jest
https://jestjs.io/docs/en/expectユニットテスト — Vuetify.js
https://vuetifyjs.com/ja/getting-started/unit-testing/
- 投稿日:2020-03-21T13:42:56+09:00
【Vue.js】スコープ付きスロットが理解しきれない人のために
はじめに
簡単に使えることが魅力のVue.jsにおいて、
最初の関門になるものの1つがスロット(slot)だと思います。特に、スコープ付きスロットは、
理解が難しく感じる人が少し多い気がするので、
私なりの理解について書こうと思います。スロット(slot)について
まずは、スロットについてのおさらいです。
スロットは、特定領域の描画内容を、
親のコンポーネントにまるまるお願いしたい時に使います。夕飯の献立を「体が温まるものが食べたい〜」と母親に伝えて、
実際に何を作るかは母親に任せるようなイメージです。実際にコードで表すと、こんな感じでしょうか。
MyChild.vue<template> <div> <h2>今日の献立</h2> <slot name="hot-meal"></slot> </div> </template>それを使う親コンポーネント
MyParent.vue<template> <div> <my-child> <template v-slot:hot-meal> <h3>たまごスープ</h3> <p>賞味期限が過ぎた卵のたまごスープ</p> </template> </my-child> </div> </template>
このように子コンポーネントの
<slot name="hot-meal">
で指定した領域に、
親コンポーネントからv-slot:hot-meal
の形式でslotのnameを指定することで
描画内容をまるまる渡すことができます。
ちなみに、slotが1つの場合はnameを省略できます。スコープ付きスロットについて
スロットについておさらいしたところで、
次は本題のスコープ付きスロットについてです。スコープ付きスロットは、
子コンポーネントが持つデータをスロット内において、
親が使うことを許可する仕組みです。子供が卵を持っていると考えてください。
夕飯の時にこの卵を使ってくれてもいいなと思ってたとします。
その場合に、「この卵、使ってもいいよ」と卵に張り紙を貼っておくイメージです。コードで説明します。
子コンポーネントのslotタグにおいて、
eggという名前で、eggというデータをバインドしています。
このバインドされた属性は、スロットプロパティと呼ばれています。MyChild.vue<template> <div> <h2>今日の献立</h2> <slot name="hot-meal" v-bind:egg="egg"></slot> </div> </template>script側はこんな感じです。
MyChild.vue<script> export default { data(){ return { egg: 'うずらの卵' } } } </script>
そして親コンポーネントでは、スロットプロパティを通して、
子コンポーネントのデータを使う事ができます。下の例だと、slotPropsという名前で、スロットプロパティを受け取っていますが、
slotPropsという名前でなくても任意の名前を使えます。MyParent.vue<template> <div> <my-child> <template v-slot:hot-meal="slotProps"> <h3>たまごスープ</h3> <p>{{ slotProps.egg }}のたまごスープ</p> </template> </my-child> </div> </template>
このように、子コンポーネントのeggというデータを受け取ることができています。
まとめ
- slotは、親に描画内容を任せるためのもの
- スコープ付きスロットは、親に描画で使える材料を渡すためのもの
おわりに
「スコープ付きスロットっていうわかりづらい概念があるから、Vue.js使うのやめよう」という人が減ればいいなと思います。(そんな人いないか..)
参考
- 投稿日:2020-03-21T02:39:29+09:00
フロントエンドエンジニア(主にVue.js)に転職して学んだこと
Qiita初投稿です。
はじめに
転職して1年が経ったので、個人的な振り返りです。
未経験から主にVue.jsを学び、モダンなフロントエンド開発が学べて個人的には大きな収穫となりました。
私について
- WEB制作会社出身
- 前職はコーダー
- 2019年に転職、フロントエンドエンジニアになる
古典的なWEB制作会社にいましたので、モダンなフロントエンドを学びたいと思い、転職活動をしました。
もちろん未経験では採用してくれる企業はなかなかいないので苦戦しましたが、縁あって今の会社に転職することができました。
入社前のVue.jsの勉強方法
- 入社前、前職の有給消化期間でUdemyと本で学習
- Udemy 「Vue JS 入門決定版」https://www.udemy.com/course/learn-vuejs/
- 本「基礎から学ぶ Vue.js」https://www.amazon.co.jp/gp/product/4863542453/
1ヶ月ぐらい休める期間があったので、Udemyを見ながらコードを書きました。
それを2~3回繰り返しました。移動時間などでは本を読みました。
Udemyと本では、片方しか載ってないこともあるので、双方向から学べて良かったです。
学んだこと
主に学んだことの一覧です。
- Vue.js
- Git
- XD
- ES6
- Ajax(非同期通信)
- VueValidator
- webpack
- babel
- eslint
Vue.js
Vue.jsでデータバインディングが手軽にできるのが良いです。
Vue.jsで学んだことについては別途書きます。
Git
GitLabを使用しています。
前職はバージョン管理はしてませんでしたし、前前職はSubversionでしたので、
gitを実務で使ったことがない状態でした。高度な使い方はしていませんが、
基本的にはコミット&プル、ブランチを切る、マージリクエストでコードレビュー&マージをするなどです。ES6
プロジェクト内のコードでは古い書き方も残っていますが、アロー関数やforEach系(map,filterなど)極力新しい書き方にするようにしています。
Ajax(非同期通信)
プロジェクトではaxiosを使ったAjaxでGETやPOSTを多数使ってます。
GETやPOSTでパラメーターを投げ、返ってきたJSONを受け取り、表示させます。
VueValidator
VueValidatorでフォームの入力時にリアルタイムでバリデーションができます。
日付や期間などカスタマイズしています。
Webpack
以前はGulpは使ってましたが、Webpackは初めてでした。
Babel
プロジェクトはIE11を対応しなくてならないので助かります。
ESlint
プロジェクトではESLintを使ってます。
問題があればブラウザに出るので助かりますが、WebStormであれば事前に警告が出ます。XD
デザインをするわけではないですが、デザイナーが作ったデザインを再現するために基本操作を知る必要があります。
その他、やってること
コードを書くこと以外にやっていることです。
- サーバーサイドとの連動部分の設計やドキュメント作成
- 新規機能や追加機能の要件定義
サーバーサイドから受け渡すJSONや、コンポーネントのProps、の設計やドキュメント作成をしてます。
また、プロジェクトには企画職やディレクターはいないので、エンジニア達で作る機能を決めています。
学びたいこと
今後、学びたいことです。
- Vuex
- nuxt.js
- Vue3系
- ユニットテスト
- StoryBook
- AtomicDesign
- TypeScript
- React
- GitHub
以下、それぞれの理由です。
Vuex
プロジェクトではSPAではなくMPA(Multi page application)のためVuexは必要ないというのがあります。
Vuexもやらないとな思うところです。nuxt.js
やっぱVue.jsといえばnuxt.jsなので。
ローカルにインストールして動かしてしたり、Udemyの動画を観てますが、実践の場がありません。
何かアイデアあれば公開しようと思います。Vue3系
Vue.jsの3系が正式リリースしているかも分からない状態なので。
ユニットテスト
「テストとは?」という状態なので。
nuxt.jsをインストールするときにJestなど聞かれるので試してみたいと思います。StoryBook
StoryBookを使っている会社が多いと聞くので。
AtomicDesign
プロジェクトを構築する初期にはAtomicDesignを検討してたようですが、私がジョインしたころは崩壊してました。
TypeScript
私がプロジェクトにジョインしてからはJSDocで引数や返り値に型をを書くようにしてますが、やっぱ型を見たほうが良いと思うので。
React
Reactは本を買ったりUdemyの動画を観たり、ローカルにインストールしたりしてますが、実践の場がないので。
GitHub
プロジェクトではGitLabを使ってますが、世間はGitHubが多数派なので。
所感
学ぶことが多くて大変満足した1年でした。
ですが、課題もあるのでこの先に学んでいきたいです。
- 投稿日:2020-03-21T02:00:28+09:00
vue.tsでdata配列の書き方
はじめに
vueのプロジェクトをtypescriptを用いて書き換えていたのですが、dataの配列の書き換え方がわからず、英語で調べたら出てきたので備忘録がてら
結論
interfaceで型描いてimportしろとのことです。
例えばめちゃめちゃ適当ですがこんなdataがあったとして、これをtypescriptに書き換えるとapp.vue<script> export default { data: () => ({ humanConditions: [ { title: 'おなかすいた', hungry: true minutes: 1205 }, { title: 'おなかいっぱい', hungry: false minutes: 1300 }, ] }) } </script>このように書き換えることができます
もちろんinterfaceの部分を同じファイルに書いてもいいですがこっちの方が綺麗かと
おなじファイルに書くときはimportとexportの間に書いたらいいと思います。interface/humanCondition.tsexport interface humanConditionIndex { title: string; hungry: boolean; minutes: number; }app.vue<script lang='ts'> import { Vue } from 'vue-property-decorator'; import { humanConditionIndex } from '../interface/humanCondition'; export default class app extends Vue { humanConditions: humanConditionIndex[] = [ { title: 'おなかすいた', hungry: true minutes: 1205 }, { title: 'おなかいっぱい', hungry: false minutes: 1300 }, ] } </script>最後に
調べてて俺が知りたいんはそこじゃないんじゃああああああってなってたのが、正しいかはわかりませんが解決できてよかったです。
誰かの役に立てば幸いです。
一応調べて出てきた英語のはこれです↓
https://github.com/vuejs/vue-class-component/issues/137
- 投稿日:2020-03-21T00:34:38+09:00
初学者がLaravelの独学に関してまとめてみた(教本/学習サイトもまとめてます)
初学者がLaravelの独学に関してまとめてみた(教本/学習サイトも)
Laravelの独学に関して
私はLaravelの学習を1月ごろから始めました。プログラミングの学習自体は11月より始めた素人に毛がレベルの輩です。日々学習中です。
ですが、このタイミングでなぜまとめたかと言いますと。。。
①沢山学習していると自分でもアウトプットしないと考えがまとまらない。今後の学習計画が経てずらい。
②少なからず、簡易的なポートフォリオは作ることができたので、これから学習する方の参考(ロードマップ)に少しでもなれば嬉しい。以上の理由からです。
間違いなどもあるかもしれません。温かく見守っていただけますと幸いです。(また、ご教示いただけますと幸いです。)
具体的なレベル感としては以下のポートフォリオを参考にしていただければと思います。
https://chobimusic.com/laravel_craftbeers/
※ログイン機能含め、基本的な機能は実装できましたが、投稿したユーザーだけが削除&編集できる権限の実装(認可周り)などはできておりません。
※これから認可やポリシーなどの理解を深め、且つVue.jsとの連携までしたポートフォリオ作成に挑戦しようと思っている段階です。
活用した教本のまとめ
Laravelの学習において参考にした教本を購入した順番でまとめていきます。※私はスクール等には通わず、ほぼ1人で本とネットを頼りに学習しています。
(※お金がないだけ。。。泣)まず最初に手に取ったのはこの本。独学で勉強していたら誰もが目にすると思います以下の本。
①PHPフレームワーク Laravel入門 第2版
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-Laravel%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83-ebook/dp/B08625YD7H/ref=pd_aw_sbs_351_2/357-1916791-6282364?_encoding=UTF8&pd_rd_i=B08625YD7H&pd_rd_r=02491f21-b407-4270-b76b-e3574f21718c&pd_rd_w=97xhS&pd_rd_wg=0donR&pf_rd_p=1893a417-ba87-4709-ab4f-0dece788c310&pf_rd_r=CRQSKHHSFS9DS01SWGQ6&psc=1&refRID=CRQSKHHSFS9DS01SWGQ6こちらでまずは勉強しました。(逆に他に入門的なLaravel本がない気がします。)
とても参考になります。Laravelの基礎を体系的に学ぶことができます。これを読めば、それなりに入門系チュートリアルは挑戦できると思います。
※以下、バカみたいにメモったこの本の見開き1ページ目※
ただし、個人的には、ミドルウェアを割と序盤で解説されて、全く理解できかった過去があるので、順番入れ替えてユーザー認証、アクセス権限に活用する実践ベースで紹介してほしかったなと思います。
また、Webアプリ(SNSぽい感じで)を作りたいとすれば、認可など深いところまでは学べまないので足りないです。別途、学ぶ必要があります。
また、この本だけだとモチベーションが担保が難しく、また実際にどうアプリ制作に活かしていくのかも掴みにくかったので、Techpitさんの教材やネットに転がっているいろんなチュートリアルを漁っては、アプリ制作も並行してしていました。
個人的には、本で体系的に知識を身に付けることも大切ですが、本を読むより、実際に模写しながらアプリ制作やってた時のほうが知識が定着していく感じがありました。(学習サイトに関しては後述しております。効率と自分に合った学習法を見つけて勉強していきましょう。)
そして、この教本といくつかのチュートリアルでアプリ制作を終えたころに、ポートフォリオ作成に取り組みました。(正直、取り掛かるのは早ければ早いほどいいと思います。)
完成したものが既述のポートフォリオになります。ログイン機能含め、基本的な機能は実装しましたが、投稿したユーザーだけが削除&編集できる権限の実装などはできませんでした。(最近やっと理解し始めたので、徐々に修正していきます。)
正直、ここの部分までわかりやすく解説してくれる教材を当時見つけられなかった。(実際とても少ない気がします。めちゃくちゃ探しました。)
そして、さらに調べまくってようやくミドルウェア・認可・ポリシー辺りの重要性に気付き始めます。
さらなるここら辺のスキルアップを試みて、次はこの本を次に手に取ります。
②PHPフレームワークLaravel Webアプリケーション開発
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AFLaravel-Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA-%E7%AB%B9%E6%BE%A4%E6%9C%89%E8%B2%B4-ebook/dp/B07SPT6XJV/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=Laravel&qid=1584693544&s=digital-text&sr=1-4注意点としてはLaravelのバージョンが5.5で記載されていることです。認証周りなどで多少バージョン6と異なる部分があります。
手に取ったときは、よくチュートリアルで紹介されているVagrant、Laradockに関しても学べるので一石二鳥だと思っていました。
ところがどっこい。
第1章の環境構築でいきなり躓く。。。笑笑
それ以外にも総じて難しく、投げ出してしまいました。(おい)
この経緯があって、今現在までXAMPPで頑張っている感じです。
その後、一時はモチベーションがかなり落ちてしまって、他言語に浮気とかもしていました。
ですが、結局既述のポートフォリオ以上のものを作れるイメージは湧きませんでした。
結局、どの言語を使ったとしても、ミドルウェアなどの知識を身に付けないと更なる向上はないと気づきました。一言語で理解を深めようと決めました。
(どの言語もベースとなる考え方は同じ!!)で、この本より少し簡単そうだなという理由でずっとレビューでは賛否両論だったので避けていたこの本にも手を出します。
③PHPフレームワーク Laravel実践開発
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-Laravel%E5%AE%9F%E8%B7%B5%E9%96%8B%E7%99%BA-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83-ebook/dp/B07WW45Q1X/ref=pd_aw_sbs_351_1/357-1916791-6282364?_encoding=UTF8&pd_rd_i=B07WW45Q1X&pd_rd_r=02491f21-b407-4270-b76b-e3574f21718c&pd_rd_w=97xhS&pd_rd_wg=0donR&pf_rd_p=1893a417-ba87-4709-ab4f-0dece788c310&pf_rd_r=CRQSKHHSFS9DS01SWGQ6&psc=1&refRID=CRQSKHHSFS9DS01SWGQ6ですが、結局まだあまり読めていません。というより改めてこの本をパラパラめくっていて思ってしまったんです。
さっさと次のポートフォリオを作ったほうが良い!!ってことに。笑 ←now!!です。
ちなみにVue.jsなどフロント言語フレームワークとの連携の記述もありますが、かなり導入部分についてしか触れられていません。
ですが、Techpitの教材などで理解できなかった際の参考に横に置いておくと安心感があります。
(たまに参考にします。)Laravel+Vue.jsのポートフォリオ作成へ挑戦したいとのことでしたら、以下のTechpit教材などで実際に作りながら学ぶことをおすすめします。(ちなみに②の本でもフロント言語フレームワークとの連携に関しては触れられていません。Laravel特化。)
https://chobimusic.com/laravel_vue_sns/
また、Vue.jsの基本的な知識もマストで必要になります。以下の猫本が有名&おすすめです。(私も学習中です。一緒に頑張りましょう。)
https://chobimusic.com/vue_nekotodo_arrange/
参考にさせていただいた学習サイトまとめ
いろいろな本を手に取って学習してきましたが、やはり1から100まで完璧に身に付けながら読むのは骨が折れます。というよりも、いきなり初学者が全て理解しようとするのは無理だと思います。モチベ担保&知識の関連付けにも、前述の本などと並行して以下のような学習サイトで実際にアプリ作成することをおすすめします。
・Techpit学び放題プラン(月額2980円)
https://techpit.thebase.in/items/24273300多種多様なアプリを作りながら学習することができます。このサイトと出会えたことで独学でここまでこれたと言っても過言ではありません。※一部単発購入が必要になるものもアリ。
・入門Laravelチュートリアル(無料)
https://www.hypertextcandy.com/laravel-tutorial-introductionToDoアプリ制作。終盤ポリシーやエラー画面の実装までします。ここまで扱う無料コンテンツはこれだけ??ありがたすぎます。ちなみにLaravel+Vue.jsのチュートリアルもあります。こちらはVueRouterなどVue.jsに関する理解をしっかりしていないと非常に難しいため、私もまだ手が届いておりません。。。泣
・【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)
https://qiita.com/namizatop/items/d79017aa474966244073
SNS系無料チュートリアル。個人的にはとても参考になりました。(作者もNBA好きぽいのでシンパシーを感じます。)
おわりに
以上です。改めて自身のアウトプットの拙さにも嫌気がさしましたが、少しでもどなたかの参考になりましたら幸いです。肝心の今後に関してですが、まだ読破できていない教本2冊とTechpitの教材を参考にしながらVue.jsまで連携させたポートフォリオ制作に挑戦しようと考えています。
※現在3/21(日)このタイミングでTechpitでLaravel+Vueに関して学べる教材(前述)がリリースされたのは本当にラッキーです。このような教材を待望してました。皆さんもチェックしてみてください◎
また、Techpit教材でのMailhog実装失敗などの経験から、XAMPP環境のみならず、Laradock(もしくはVagrant)での環境構築もできたほうが良いなと思っています。こちらに関しては、改めて②の本とネットの力をお借りして、引き続き挑戦していきたいと思っております。(※Dockerの本も探しましたが、Laradockに関しては扱っている本も少なくググったほうが参考資料が多いなと感じました。)
最後まで読んでいただきありがとうございました。
おしまい。
- 投稿日:2020-03-21T00:28:29+09:00
Vue.jsのコンポーネントでv-modelを使う
自作したコンポーネントにもv-modelを実装できる。
その実装方法を説明する。文字入力UIのデザインを統一する想定で、UiTextInputというコンポーネントを作成する。
文字入力UIのデザインを統一するために実装する機会は多そう。そうでもない?Vuetifyを使う?結論はこちら
UiTextInputの実装
v-modelを実装するには、
- propsプロパティにvalueを設定して、親コンポーネントから値を受け取る。
- inputイベントを$emit(通知)して、親コンポーネントに変更後の値を渡す。
必要がある。
そのため、computedにgetter/setterを作成し、inputのv-modelとの値の受け渡しを行うようにした。
- getterでpropsのvalueを返却して、親コンポーネントから値を受け取る。
- setterでinputイベントを$emitして、親コンポーネントに変更後の値を受け渡す。
実装したソースコードが以下である。
<template> <input v-model="text" placeholder="入力してください"> </template> <script> export default { props: { value: { type: String } }, computed: { text: { get() { return this.value; }, set(text) { this.$emit("input", text); } } } } </script> <style scoped> input { display: block; border-width:0px; border-style:None; font-size: 18px; background-color: rgba(0,0,0,0); outline: 0; border-bottom: 1px solid #d0d0d0; } </style>使い方
通常のinputのv-modelと同様に使用できる。
UiTextInputコンポーネントを呼び出して、v-modelに値を指定する。<template> <div> <h2>v-modelのサンプル</h2> <div class="field"> <label>名前 </label> <UiTextInput v-model="text" class="input" /> </div> <p v-if="text">こんにちは、 {{text}} さん!</p> </div> </template> <script> import UiTextInput from "./UiTextInput.vue"; export default { components: { UiTextInput }, data() { return { text: "" }; } } </script> <style scoped> .field { display: flex; } label { flex-basis: 100px; } .input { flex-grow: 1; } </style>データフロー
文章とソースコードによる説明だとイメージしづらいかもしれないので、補足としてデータフローで図解する。
使用する側(親コンポーネント)からUiTextInput(子コンポーネント)へ値を渡す
UiTextInput(子コンポーネント)から使用する側(親コンポーネント)へ値を渡す
結論
コンポーネントでv-modelを実装する際は、
- propsのvalueで、親コンポーネントから値を受け取る。
- inputイベントを$emit(通知)して、親コンポーネントに値を渡す。
をすれば良い。