v-slotの使い方が知りたい。

早くGithub Actionsを実装して脳死デプロイができるようになりたいです。

今回は先人PFによく出てくるv-slotについて学習していきたいと思います。

 

v-slotとは

親となるコンポーネント側から、子のコンポーネントのテンプレートの一部を差し込む機能

また親子間の値のやり取りに関するディレクティブが出現してきた。親⇨子というこは、propsと同じなのかな?

propsとの違い

propsは基本的に値を渡すのに対して、slotでは描画内容を渡します。

(https://greko-engineer.hateblo.jp/entry/2019/11/30/195237)

値だけでは無く、タグそのものも渡すことができるってわけかな。

API - Vue.js

どんな旨みがあるの?

この部分に、こういう役割のものを表示したい。けれど、どのように表示するかは、このコンポーネントを使う側で決めてほしい」時に旨みがでる。

ユーザーのログイン状態に合わせてヘッダーの状態を変えるときに使える。

例えばv-ifを使ってアクションの状態に応じてボタンの状態を変えたい場合は、下記のように条件分岐を書くが、これだと条件分岐の数が多くなってしまう。

<template>
  <div v-if="currentScreen === 'home' ">
    <star-button/>
  </div>
  <div v-else-if="currentScreen === 'search' ">
    <setting-btn/>
  </div>
</template>

だから、slotを使って、親コンポーネントに描画する内容を任せたい場面が出てくる。

書き方は?

子に渡したい値を子コンポーネント名で囲む。

#src/Parent.vue
<template>
  <div class="home">
    <DefaultLayout>酒ケジュールを提供します。</DefaultLayout>
#子コンポーネントに書かれている「最強のアルゴリズムを採用しています。」が優先される。
  </div>
</template>

<script>
import DefaultLayout from '../components/DefaultLayout.vue'
export default {
  components: {
    DefaultLayout
  }
}
</script>

slotを使わないと、コンポーネントに書かれている値が優先される。

この値をフォールバックコンテンツと言う。

#src/components/DefaultLayout

<template>
  <div class="DefaultLayout">
   <p>最強のアルコリズムを採用しています。</p>
  </div>
</template>
<style>
</style>
#表示される内容
最強のアルコリズムを採用しています。

コンポーネント側のテンプレートに<slot>タグを記述すると、その場所ではスロットコンテンツが埋め込まれる。

#src/Parent.vue
<template>
  <div class="home">
    <DefaultLayout>酒ケジュールを提供します。</DefaultLayout>
#子でslotが使われているため、「酒ケジュールを提供します。」が優先される。
  </div>
</template>

<script>
import DefaultLayout from '../components/DefaultLayout.vue'
export default {
  components: {
    DefaultLayout
  }
}
</script>

親の状態に合わせて描画内容を変えたい場所をslotで囲む。

#src/components/DefaultLayout

<template>
  <div class="DefaultLayout">
   <p><slot>最強のアルコリズムを採用しています。</slot></p>
  </div>
</template>
<style>
</style>
#表示される内容
酒ケジュールを提供します。

親側でスロットコンテンツ(DefaultLayoutで囲まれている部分)が定義されていた場合は、

<slot>タグで囲まれたコンポーネント側のコンテンツ(「最強のアルコリズムを採用しています。」)は表示されず、親側のスロットコンテンツが表示される。

名前付きスロット

複数のスロットがあると便利なときもある。

<default-layout> コンポーネントが下記のようなテンプレートだった場合を考える。

#src/components/DefaultComponent.vue

<div class="container">
  <h3>
    <!-- ここに英語のコンテンツ -->
  </h3>
  <h3>
    <!-- ここに日本語のコンテンツ -->
  </h3>
</div>

複数のslotを追加することで、親コンポーネントで呼び出した時に、動的にコンテンツを描写することができる。

#src/components/DefaultComponent
<div class="container">
  <h3>
    <slot name="eng">I love you.</slot>
  </h3>
  <h3>
    <slot name="jpn">ありがとう、私もよ。</slot>
  </h3>
</div>

name のない <slot> 要素は、暗黙的に「default」という名前を持つ。

<template> に対して v-slot ディレクティブを使って、スロット名を引数として与える。

そうすることで、slotにコンテンツを渡すことができる。

#src/Parent.vue

<DefaultLayout>
</DefaultLayout>

<DefaultLayout>
  <template v-slot:eng>
    <h3>I hate you.</h3>
  </template>
</DefaultLayout>

<DefaultLayout>
  <template v-slot:jpn>
    <h3>悲しいわ。</h3>
  </template>
</DefaultLayout>

<script>
import DefaultLayout from '../components/DefaultLayout.vue'
export default {
  components: {
    DefaultLayout
  }
}
</script>

表示される内容

I love you.
ありがとう、私もよ。

I hate you.
ありがとう、私もよ。

I love you.
悲しいわ。

名前つきslotの省略記法

v-bind を 「:」、v-onを「@」で省略できるように、v-slotも「#」で省略して記述することができる。

#src/Parent.vue

<DefaultLayout>
</DefaultLayout>

<DefaultLayout>
  <template #eng>
    <h3>I hate you.</h3>
  </template>
</DefaultLayout>

<DefaultLayout>
  <template #jpn>
    <h3>悲しいわ。</h3>
  </template>
</DefaultLayout>

<script>
import DefaultLayout from '../components/DefaultLayout.vue'
export default {
  components: {
    DefaultLayout
  }
}
</script>

スコープ付きslot

スコープを使うと、スロットコンテンツから、子コンポーネントの中だけで利用可能なデータにアクセスできる。

コープ付きslotの記述方法

スコープ付きslotを利用するには、子コンポーネント側では、<slot>タグに対してv-bindをしようする必要がある。

コンポーネント内でスロットコンテンツとして user を使えるようにするために、<slot> 要素の属性として user をバインドする:

#src/components/CurrentUser.vue
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
<script>
export default {
  name: 'CurrentUser',
  data () {
    return {
      user: {
        lastName: 'nakano',
        firstName: 'suabaru' // ←slot内で参照したいデータ
      }
    }
  }
}
</script>

<slot> 要素にv-bindでバインドされた属性をスロットプロパティ と呼ぶ。

親スコープ内で v-slot の値として名前を指定することで、スロットプロパティを受け取ることができる。

<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

slotProps は自分の好きな名前に変換することができる。

まとめ

  • v-slotを使うことで、描画したい内容を親コンポーネントに任せることができる。
  • 動的に値を変更したい箇所は<slot>で囲む
  • v-slotは#,v-onは@,v-bindは:で省略することができる。
  • 酒ケジュールとは、お酒の飲む順番の造語で、酒 + スケジュールを掛け合わせた言葉である。

スロット - Vue.js

https://greko-engineer.hateblo.jp/entry/2019/11/30/195237

Vue.jsのslotの機能を初心者にわかるように解説してみた | フューチャー技術ブログ

Vuexに関する用語集

状態を管理してくれるらしい。状態って何。分からない。

いつも通り、アウトプットありきのインプットをしていこうと思う。

Installation | Vuex

Vuexとは

状態管理ライブラリ。データフローが単方向になるように設計されている。

大きなアプリケーション開発に使われることが多いらしい。

例えばログインの状態を全てのページで管理したい場合、このライブラリが使えるらしい。

コンポーネントで定義していた算出メソッドやルーティング等をStoreという場所に集約している。

mapGettersやgettersを使うことで、コンポーネントの受け渡しをスムーズにしてくれている。

this.$store.getters.メソッド名

Store

Vuexの根幹部分。1アプリにつき最大1つ存在する。

new Vuex.Store({⇦ここ
	state{},
	getters{},
	mutations{},
	actions{},
})

この中に大きく4つのメソッドがある。

・state ・getter(値を返す) ・mutation(stateを更新する) ・action(非同期処理や外部APIとのやりとりを行う

この4つそれぞれが働きあってコンポーネント間で値の状態管理がうまい具合に行われているとのこと。

下記のような流れで値の状態管理が行われいる。

コンポーネント⇨②アクション⇨③ミューテーション⇨④ステート⇨⑤ゲッター①に戻る

コア機能

State

アプリケーションレベルの状態が全て含まれており、"信頼できる唯一の情報源 (single source of truth)" として機能します

使い方

// let's create a Counter component
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

store.state.countが変化するたびに、computedにあるpropertyがリアクティブに変化する、それに連動して、DOMが更新される。

rootディレクトリ(storeファイル)に下記コードを記述することで、子コンポーネントの変化を全てのコンポーネントに自動で反映させることができる。this.はself.と同じ感じ?

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

this.$storeを前につけることで、Storeにアクセスできる。

Getters

Storeに入っている算出プロパティやルーティングにアクセスできる中継地点。その名の通り、ゲッターとしての役割を持つ。

例えばこの算出プロパティを他のコンポーネントで使用したい時があるとする。

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

ここでgettersの登場。

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

他のコンポーネントは、computedの中にthis.$store.getters.doneTodosを書き込むだけで、doneTodesを使用することができる。

mapGetters

...mapGettersを使うことで、$store.gettersを省略することができる。

this.$store.getters.getter1 //通常
this.getter1 //mapGetters使用

実際の使い方。

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    // ゲッターを、スプレッド演算子(object spread operator)を使って computed に組み込む
    ...mapGetters([
      'doneTodosCount',
			#本来はthis.$store.getters.doneTodosCountと書く必要がある。
      'anotherGetter',
      // ...
    ])
  }
}

Mutations

特徴

  • 同期処理である必要がある。
  • stateを変更することができる。
  • 直接呼び出すことができないので、store.commitにミューテーション名や引数(ペイロードと呼びます)を与えて呼び出すことでstateを更新することができる。
const store = new Vuex.Store({
  state: {
    count: 10
  },
  mutations: {
    increment (state) {
      // 状態を変更する
      state.count++
    }
  }
})
console.log(store.state.count)//→10
store.commit('increment')//incrementミューテーションを呼び出す
console.log(store.state.count)//→11

store.commitの第二引数に値を与えると渡される。(この値をペイロードと呼ぶ)

mutations:{
    increment(state,payload){
        state.count = state.count + payload.amount
     }
}
console.log(store.state.count)//→10
store.commit('increment',{amount:5})//
console.log(store.state.count)// →15

Actions

下記の特徴を持ちます。

  • アクションは、状態を変更するのではなく、ミューテーションをコミットします。
  • アクションは任意の非同期処理を含むことができます。
  • 非同期処理や外部APIとの通信を行い、最終的にミューテーションを呼び出すために使う
//アクションを定義
actions:{
    incrementAction(ctx){
//incrementミューテーションを実行する
        ctx.commit('increment')
     }
 }

ミューテーションと同様に直接呼び出すことはできない。

だから、store.dispatchにアクション名を与えて呼び出す。

#初期の値
console.log(store.state.count)// -> 10
#直接アクセスすることができないので、dispatchを使用する。
store.dispatch('incrementAction')//incrementActionを呼び出す
#もう一度呼び出してみる。
console.log(store.state.count)// ->11

Redux,Flux

Flux(フラックス)はFacebook社が提唱している、クライアントサイドのWebアプリケーション開発のためのアプリケーション・アーキテクチャ(設計思想)です。 単方向のデータフローを構築できることが最大の特徴で、開発の規模が大きくなってもデータの流れを見失いづらいことが大きなメリットです。

ReduxはReactの状態管理ライブラリ。

もともとVuexはfacebook社が開発したFluxという技術からインスパイアされている。

まとめ

  • Vuexは状態管理ライブラリ。データフローが単方向になるように設計されている。
  • Storeという大きな箱の中に4つのプロパティを保管している。
  • Stateでアプリの状態を管理している。
  • GettersはStoreに入っている算出プロパティやルーティングにアクセスできる中継地点。
  • ...mapGettersを使うことで、$store.gettersを省略することができる。
  • Mutationsで値を更新することができる。
  • Actionsで外部APIを叩くことができる。

ステート | Vuex

ゲッター | Vuex

アクション | Vuex

ミューテーション | Vuex

Vuexをわかりやすく知りたい|あきな@旅、本、プログラミング。|note

親子コンポーネント間の受け渡し

コンポーネントを渡す流れが分からない、、

props?$emit?他のファイルに値を渡す作業って難しい。

どうやらpropsと$emitという概念があるらしい。

文献を参考にして頭に入った感を味わうために今日もVueの学習を進めていく。

 

props

配列でdataのプロパティを渡す

親⇨子にコンポーネントを渡すときはpropsを使う。

紛らわしいのが、親⇨子という流れなのに、コンポーネントをインポートしているのは親だということ。日本語的に、コンポーネントを「渡される」対象が子なら、子がインポートするのが普通なのではと思う。

#src/components/Child.vue

<template>
  <div>
    <p>{{ statement }}</p>
  </div>
</template>

<script>
export default {
  props: {
    statement: {
     type: String,
     default: 'hogehoge'
    }
  }
#propsの名前と型を子コンポーネント内で定義している。
}
</script>

補足:

propsの名前と型を子コンポーネント内で定義している。

#src/pages/App.vue
<template>
  <div>
    <h1>Hello from App.vue</h1>
    <Child statement='choose to go to the moon.'/>
#ChildのgreetをHello with propsという値に上書きしている。
  </div>
</template>

<script>
import Child from './components/Child.vue'
#子コンポーネントを親コンポーネントがインポートしている。
export default {
  components: {
    Child
#childにコンポーネントを渡している。
  }
}
</script>

補足:

ChildのgreetをHello with propsという値に上書きしている。

コンポーネントを親コンポーネントがインポートしている。

ポイント

$emit

コンポーネントのデータを親コンポーネントへ渡し、親コンポーネントのイベントを発火させることができる。

子⇨親にコンポーネントを渡している。

src/components/Child.vue
<template>
  <div>
    <p>subordinate_num: {{ subordinate_num }}</p>
    <button @click='send'>君主に値を渡す</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      subordinate_num: 0
    };
  },
  methods: {
    send() {
      this.$emit("my-click", this.subordinate_num);
 #親コンポーネントでは、子で定義していたsend()アクションをmy-clickという名前で実行できる。
    }
  }
};
</script>

補足:

	this.$emit("my-click", this.subordinate_num);

とすることで、親コンポーネントでは、子で定義していたsend()アクションをmy-clickという名前で実行できる。

#src/pages/App.vue
# パターン1:受け取った値をそのまま使う場合は$eventで受け取る
<template>
  <div>
    <h1>Hello from App.vue</h1>
    <p>lord_num: {{ lord_num }}</p>
    <Child @my-click='lord_num = $event'/>
#受け取った値を@に続いた形で記述。
#バインドさせた値 = $eventと書くことで、そのまま使用できる。
  </div>
</template>

<script>
import Child from './components/Child.vue'
export default {
  data() {
    return {
      lord_num: 100
    }
  },
  components: {
    Child
  }
}
</script>

補足:

受け取った値を@に続いた形で記述。 バインドさせた値 = $eventと書くことで、そのまま使用できる。

src/App.vue
# パターン2. 受け取った値を関数で使う場合は、適当な変数(value)を定義するとそこに値が入る
<template>
  <div>
    <h1>Hello from App.vue</h1>
    <p>lord_num: {{ lord_num }}</p>
    <Child @my-click='citedNum'/>
  </div>
</template>

<script>
import Child from './components/Child.vue'
export default {
  data() {
    return {
      lord_num: 100
    }
  },
  components: {
    Child
  },
  methods: {
    citedNum(value) {
      this.lord_num = value
    }
  }
}
</script>

ポイント

  • コンポーネント内で、$emitでカスタムイベント(clickとかchange的な使われ方をするやつ)を作る
    • 第2引数に送信するデータを渡す
  • コンポーネント内で子コンポーネントを呼び出す時に、作成したカスタムイベントを付与する
  • $eventや引数で送信されたデータを受け取る

まとめ

props

$emit

  • コンポーネント内で、$emitでカスタムイベント(clickとかchangeのような使われ方をするもの)を作る
    • 第2引数に親に送信するデータを渡す
  • コンポーネント内で子コンポーネントを呼び出す時に、作成したカスタムイベントを付与する
  • $eventや引数で送信されたデータを受け取る

propsと$emitでデータを引き渡す - Qiita

コンポーネントの基本 - Vue.js

A Guide to Vue $emit - How to Emit Custom Events in Vue

v-modelディレクティブ

v-〇〇の種類が多くて頭がパンクしそうです。脳をオンプレミスからクラウドに移行したいことスバルです。今回はv-modelについて学習していきます。

v-modelとは

dataオブジェクトのプロパティの値とinputタブの入力値や選択値のデータを連動することができる。

(参考: https://qiita.com/shizen-shin/items/d9f8c8c9a74618c0b326)

form の input 要素 や textarea 要素、 select 要素に双方向 (two-way) データバインディングを作成できる。双方向バインディングとも呼ばれる。

ユーザーの入力に応じて等のイベントに基づいて、バインディングしたデータをリアクティブデータに反映させる処理が肝。

注意点

任意の form 要素にある value、checked、または selected 属性の初期値を無視する。

使用例

case) textarea

HTML
<div id = "app">
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
</div>
js
var app = new Vue({
    el:'#app',
    data:{
        message: ""
    }
})

textareaに入力した内容が、messageの部分に表示される。

case) radio button

<input type="radio" v-model="プロパティ名" value="固有の値">

プロパティ名:'チェックしたい要素のvalue'

html
<div id="app">
        <div>
            ■ラジオボタンとv-model
            <br><input type="radio" id="good" v-model="radio" value="good">
            <label for="good">good</label>

            <input type="radio" id="normal" v-model="radio" value="normal">
            <label for="normal">normal</label>

            <input type="radio" id="bad" v-model="radio" value="bad">
            <label for="bad">bad</label>

            <p>プロパティの状態: {{radio}}</p>

        </div>
   </div>
var app = new Vue({
    el:'#app',
    data:{
        //ラジオボタン
        radio: 'bad'
    }
})

修飾子

.lazy

inputに入力して、エンターなどを押した後に値が反映される。

遅延評価するときは大体lazy

<input v-model.lazy="msg">

.number

ユーザの入力を Number として自動的に型変換したいときに使う。

<input v-model.number="age" type="number">
#上下矢印とともに、数字が際限なく表示される

.trim

ユーザーが入力した空白を取り除くことができる。

<input v-model.trim="msg">

v-bindとv-modelの違い

v-bind は Model の値を HTML コンポーネントに反映(出力)します。HTML コンポーネントの値が変わっても、Model の値は変わりません。Model から HTML への一方通行です。

一方、v-model は Model と View(HTML)の双方向に影響します。 HTML コンポーネントの値に変更があった場合、自動で Model の値を更新します。

入力内容がモデルに反映されるのがv-modelらしい。束縛か、相思相愛ってことか。

<div id="app">
        v-bind[{{ data1 }}]
        <div><input type="text" v-bind:value="data1"></div>
        <br><br>
        v-model[{{ data2 }}]
        <div><input type="text" v-model="data2"></div>
    </div>
var app = new Vue({
    el: '#app',
    data: {
        data1: "data1",
        data2: "data2"
    }
})

data1に何かを入力しても、初期値から変化はない。

一方で、data2に何かを入力したら、何かに沿って変化する。

ラジオボタン(input[type="radio"])を操作する [Vue.js]

【Vue.js】「フォーム入力バインディング」である「v-model」について

ライフサイクルフックとは

他の方のポートフォリオを見ていると、mountedやcreatedなど、見知らぬ用語が頻出してちんぷんかんぷん。調べてみると、Vueにはライフサイクルフックなる概念があり、理解したいと思ったから学習する。

ライフサイクルフックとは

Vueが初期化されてから削除されるまでの一連の流れのこと。

new Vueから始まり、destroyで終わる。Vueの一生のこと。Vue.js公式に載っている図を見ると理解しやすい。f:id:subaru-hello:20211008165452p:plain

ざっくりした流れ

new Vue()を作成

beforeCreateで、初期化前の処理を記入

initで初期化される

createdでAPIを叩いたり、ローディング画面を実装

elによって指定された箇所があるかを識別

elがない場合、mount関数が実行される

template部分があるかを識別し、renderとくっつける

or

templateがない場合、elオプションで定義されたDOMをtemplateとしてコンパイルする。

mountでインスタンスのelement(vm.$el)を作成し、elオプションに置換する

 

(Vue インスタンス — Vue.js)

主要項目の説明

new Vue()

vueの初期化を行う処理。

主に、hello_vue.jsのようなメインファイルに記述することで、virtual DOMの構築をする。下記のように書く。

import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    router,
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)
})

created

初期化の後に実行される。

APIを呼んでサーバなどからのデータ取得処理を書くことが多い

  • インスタンスの初期化が済んで props や computed にアクセスできる
  • DOMにはアクセスできない

( 参考文献: https://www.kimullaa.com/entry/2019/12/01/132724)

使用例

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` は vm インスタンスを指します
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"
  1. new VueでVueインスタンスを作成(以降vmで表現できる?)
  2. createdでdataにアクセス
  3. console.logでメソッドを実行

mounted

VueインスタンスとDOMが紐付けされる。

this.$elによってDOMが作成される。createdだとまだDOMにはアクセスできないが、mountedの段階だとアクセスすることができる。

https://qiita.com/kaorina/items/bb261a119b9f02e02c2d

https://greko-engineer.hateblo.jp/entry/2019/12/21/183509

createdとmountedの違い

createdはDOMがまだ作られていない状態で、mountedではDOMが作成された直後の状態です。

頭に画像をイメージしながら実装できると、scriptが描きやすそう。

el

el: '#app'は、documentElementById('app')というDOM操作をしているのと同義

基本形

#appの中のデータを操作する。

var app = new Vue({
  el: '#app',
  data: {
  },
})

実際に使ってみる

<body>
  <div id="app">
    <button v-on:click="emargeEl">Push Here</button>
  </div>

<script src="<https://cdn.jsdelivr.net/npm/vue/dist/vue.js>"></script>
<script>
new Vue({
  el: '#app',
  data: {
  },
  methods:{
    emargeEl : function(){
     console.log(this.$el.innerHTML = "<h1>Good Night World</h1>")
    }
  }
})
</script> 
</body>
  1. v-on:click="emargeEl"は、クリックしたらemargeElメソッドが実行されることを指している。
  2. console.log(this.$el.innerHTML = "<h1>Good Night World</h1>")で指定した箇所のHTMLを<h1>Good Night World</h1>に変更している。this.$elは#appを指している。

Image from Gyazo

Vue インスタンス - Vue.js

vue.jsのcreatedとmountedの違いを目で見て理解 | アールエフェクト

モーダル機能と学ぶ(transition,props,emit)

Vueの4つの特徴(コンポーネントトランジション、拡張性、リアクティブネス)の一つで

あるトランジションについて、モーダルの実装を通して学習していきたいと思う。

transitionって何?

Vue は、transition ラッパーコンポーネントを提供しています。このコンポーネントは、次のコンテキストにある要素やコンポーネントに entering/leaving トランジションを追加することを可能にします:

動きをつけたい箇所をtransitionで囲むってことなのかな?

そんでもって、動きをつけるかどうかは、v-ifを使って表すこともあるらしい。

v-ifに関して、

trueの時は、cssで定義したenter-active・leave-active、

falseの時はleave-to・enter

になる。

では、モーダルを使って考えてみる。

Enter/Leave とトランジション一覧 - Vue.js

どうやって使うの?

index.html

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

v-on:clickで、showと!showの切り替えを行なっている。

v-onを使うとイベントハンドリングができるらしい。

つまり、Toggleをclickすると、showと!showの切り替えが行われるということになる。

ほんでもって、transition内にイベントの対象を記述している。

もしshowの場合、helloはデフォルトの状態のままで、!showだとcssのfade-enter-to

,fade-leave-toが適用される。(cssの内容に関しては後述する)

 #main.js
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})

今回は、v-if = showがtrueだからhelloがデフォルトで表示されていることを指している。

デフォルトでは、showがtrueになっている。

stylesheet.css

.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

この、enterやleaveというのは、トランジションクラスというらしい。

下記に引用文献を貼っておく。

これらは、enter/leave トランジションのために適用される 6 つのクラスです。

  1. v-enter: enter の開始状態。要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除されます。
  2. v-enter-active: enter の活性状態。トランジションに入るフェーズ中に適用されます。要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの開始に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
  3. v-enter-toバージョン 2.1.8 以降でのみ利用可能です。 enter の終了状態。要素が挿入された 1 フレーム後に追加され (同時に v-enter が削除されます)、トランジション/アニメーションが終了すると削除されます。
  4. v-leave: leave の開始状態。トランジションの終了がトリガされるとき、直ちに追加され、1フレーム後に削除されます。
  5. v-leave-active: leave の活性状態。トランジションが終わるフェーズ中に適用されます。leave トランジションがトリガされるとき、直ちに追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの終了に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
  6. v-leave-toバージョン 2.1.8 以降でのみ利用可能です。 leave の終了状態。leave トランジションがトリガされた 1 フレーム後に追加され (同時に v-leave が削除されます)、トランジション/アニメーションが終了すると削除されます。

vに、transition name="〇〇"の〇〇部分を入れた状態で使用するみたいだ。

今回の例だと、fadeを入れているから、fade-enterやfade-leave-toと表している。

成果物

Image from Gyazo

props

コンポーネントから子コンポーネントに値を渡したい時に使う

props

  • 型: Array<string> | Object

  • 詳細:

    コンポーネントからデータを受け取るためにエクスポートされた属性のリスト/ハッシュです。シンプルな配列ベースの構文、そして型チェック、カスタム検証そしてデフォルト値などの高度な構成を可能とする配列ベースの代わりとなるオブジェクトベースの構文があります。

親⇨子

使用例

// シンプルな構文
Vue.component('props-demo-simple', {
  props: ['size', 'myMessage']
})

// バリデーション付きのオブジェクト構文
Vue.component('props-demo-advanced', {
  props: {
    // 単なる型チェック
    height: Number,
    // 型チェックとその他のバリデーション
    age: {
      type: Number,
      default: 0,
      required: true,
      validator: function (value) {
        return value >= 0
      }
    }
  }
})

v-for

繰り返し処理を記載する。

idがblog-post-demo

Postテーブルのデータで、idが1~3の各タイトル表示させたい。

その場合、script内に下記記述をする。

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})

template内に下記記述をする。


<div id="blog-post-demo">
	<blog-post
	  v-for="post in posts"
	  v-bind:key="post.id"
	  v-bind:title="post.title"
	></blog-post>
</div>

すると、下記のようなアウトプットが得られる

My journey with Vue
Blogging with Vue
Why Vue is so fun

コンポーネントの基本 - Vue.js

プロパティ - Vue.js

emitって何?

コンポーネントの動きを親コンポーネントが察知するためのメソッド。

子⇨親

vm.$emit( eventName, […args] )

  • 引数:

    • {string} eventName
    • [...args]
  • 使用方法:

    現在の vm 上のカスタムイベントを監視します。イベントは vm.$emit によってトリガすることができます。それらのイベントトリガを行うメソッドに渡した追加の引数は、コールバックがすべて受け取ります。

  • 例:

    vm.$on('test', function (msg) {
      console.log(msg)
    })
    vm.$emit('test', 'hi')
    // => "hi"
    

$emitと$onはセットで使用する。

イベントと$emitを一緒に使うこともできる

$emit をイベント名のみと共に使う場合:

Vue.component('welcome-button', {
  template: `
    <button v-on:click="$emit('welcome')">
      Click me to be welcomed
    </button>
  `
})

welcome-buttonというコンポーネントを作った。template:以下を保持している。その保持している部品の中には、クリックされるとwelcomeを発火するbuttonが格納されている。

<div id="emit-example-simple">
  <welcome-button v-on:welcome="sayHi"></welcome-button>
</div>

HTMLにemit-example-simpleというタグをつけたdivをかく。

importしたwelcome-buttonコンポーネントにクリックするとwelcomeが実行されるメソッドをかく。

new Vue({
  el: '#emit-example-simple',
  methods: {
    sayHi: function () {
      alert('Hi!')
    }
  }
})

Vueインスタンスの中に、sayHiファンクションを書く。

まとめ

コンポーネント分割を行う際に片方のコンポーネントがもう片方のコンポーネントを取り込み(import)する時、importする側を「親(コンポーネント)」と呼び、される側を「子(コンポーネント)」と呼びます。

(参考文献: https://recruit.cct-inc.co.jp/tecblog/vue-js/vue-emit-props/)

propsと$emitでデータを引き渡す - Qiita

API - Vue.js

カスタムイベント - Vue.js

コンポーネントの基本 - Vue.js

API - Vue.js

VueでAPIを叩く(axios,fetch())

脳みそをオンプレミスではなくクラウドに移行したいことスバルです。

前々から気になっていたAPIcurlを入力するとjsonが返ってきて、それを読み込んでviewにjsonの中身を表示させるものかな?くらいの認識だから、もっと体型的に学んでいこうと思う。

それでは、VueでAPIを叩く(axios,fetch())を深掘っていきましょう。

APIとは

アプリと外界を繋ぐ中継役のこと。認証と認可でも出てきた。

アプリケーションからリクエストを送ったら、サーバーからjson形式データが返ってくる。

この送る場所と返ってくる場所の交流地点?薩摩藩長州藩の仲介役だった坂本龍馬的な?

つまりAPIとは坂本龍馬

VueでAPIを叩くには?

APIを叩く用の外部リソースであるaxiosもしくは、外部APIであるFetch APIを使用する。

axiosとは

公式によると、下記のポイントが特徴とされている。

XMLを使っている

  • Make http requests from node.js

node.jsからhttpリクエストを送っている

  • Supports the Promise API
  • Intercept request and response, Transform request and response data

リクエストとレスポンスの中継地点として双方のデータを変換している

  • Cancel requests, Automatic transforms for JSON data

リクエストをキャンセルすることも、JSONデータを自動的に変換することもできる。

  • Client side support for protecting against XSRF

そして、クロスサイトリクエストフォージェリから守ることができる。

一般的なアプローチは Promise ベースの HTTP クライアントの axios を使うことです。

APIを叩くときにaxiosがインターフェイスになってくれるみたい。

実際に使ってみる

yarn add axios
yarn add v1.22.10
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning " > bootstrap@4.0.0-beta" has unmet peer dependency "jquery@>=3.0.0".
warning " > bootstrap@4.0.0-beta" has unmet peer dependency "popper.js@^1.11.0".
warning " > vue-loader@15.9.8" has unmet peer dependency "css-loader@*".
warning " > vue-loader@15.9.8" has unmet peer dependency "webpack@^3.0.0 || ^4.1.0 || ^5.0.0-0".
warning " > webpack-dev-server@3.11.2" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > webpack-dev-middleware@3.7.3" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
warning Your current version of Yarn is out of date. The latest version is "1.22.15", while you're on "1.22.10".
info To upgrade, run the following command:
$ curl --compressed -o- -L <https://yarnpkg.com/install.sh> | bash
success Saved 1 new dependency.
info Direct dependencies
└─ axios@0.22.0
info All dependencies
└─ axios@0.22.0
✨  Done in 7.41s.

axiosをmain.jsで読み込む。

# packs/main.js 
import axios from '../plugins/axios'

Vue.prototype.$axios = axios

plugins/axiosにaxiosの取得方法を記載しておく

import axios from 'axios'

const axiosInstance = axios.create({
  baseURL: 'api'
})

export default axiosInstance

ライフサイクルフックとメソッドを活用してAPI通信をする。

#pages/task/index.vue
<script>
export default {
  name: "TaskIndex",
  data() {
    return {
   tasks: []
    }

  },

  created() {
    this.fetchTasks();
  },

  methods: {
    fetchTasks() {
      this.$axios.get("tasks") #tasksをgetメソッドで取得

        .then(res => this.tasks = res.data) #レスポンスデータをthis.tasksに格納
        .catch(err => console.log(err.status)); #エラー文処理
      ]
    }
  }
}
</script>

これでaxiosを使ったAPI通信の準備ができたみたいだ。

ちなみに、、

ライフサイクルとは

Vueインスタンスが生成されてから破棄されるまでの流れ

1.生成・初期化

  • Vueインスタンス生成後に初期化処理を行う。
  • リアクティブデータ(data)の初期化
    • dataの更新が画面の表示と同期されるようになる。
    • 対応関数:beforeCreate,created

created

APIを呼んでサーバなどからのデータ取得処理を書くことが多い。

他には、DOMへのマウントが完了して画面が描画されるまでのローディングの表示処理もここで書いたりする。

2.マウント

  • コンポーネントをDOMにマウント(VueインスタンスとDOMが紐付け)する。
  • Vueインスタンスelオプションが指定されているかを確認する。
    • 指定がある場合:次のフェーズへ移行する。
    • 指定がない場合:mount関数が実行されたタイミングで、次のフェーズに移行する。
  • templateオプションを持っているかを確認。
    • 持っている場合:templaterender関数にコンパイルされる。
    • 持っていない場合:elオプションで定義されたDOMをtemplateとしてコンパイルする。
  • マウント(インスタンスelementを作成し、elオプションに置換)する。
  • 対応関数:beforeMount,mounted

3.データ/画面の更新

  • 画面が表示されている状態。
  • データが変更されると、render関数が実行され再描画が行われる。
  • 対応関数:beforeUpdate,updated

4.インスタンスの破棄

  • インスタンスは画面遷移などで描画されなくなるタイミングで破棄される。
  • 対応関数:beforeDestroy,destroyed

(引用: https://qiita.com/KWS_0901/items/5105677462f69f197ad2)

Fetch API

Fetch APIとは、XMLHttpRequestと同じでHTTPリクエストを発行する APIですが、XMLHttpRequestよりシンプルでモダンな APIです。

Promiseを返すらしい。

リクエストとレスポンスの流れ?を作るときは、async/awaitを使って書く方法と、thenを使って書く方法があるらしい。

Promiseとは

Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。

Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシです。Promise を用いることで、非同期アクションが最終的に成功した時の値や失敗した時の理由に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値をすぐに返す代わりに、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。

※参考:Promise - JavaScript | MDN

非同期処理の完了結果を返してくれる。

async/await構文

async: 非同期

await: 待つ

async function 宣言は、 非同期関数 — AsyncFunction オブジェクトである関数を定義します。非同期関数はイベントループを介して他のコードとは別に実行され、結果として暗黙の Promise を返します。

※参考:async function - JavaScript | MDN

await 演算子は、async function によって Promise が返されるのを待機するために使用します。

※参考:await - JavaScript | MDN

//json読み込み
async function hoge () {
 const res = await fetch(url);
  const json = await res.json();
 // 処理 json.xxxx〜
}

  • 関数のfunction宣言の前にasyncを書いて非同期(async)関数であることを宣言
  • 変数resawaitを書くことで非同期通信が終わった後にfetch()メソッド(引数はurl)を実行。
  • 変数jsonawaitを書くことで非同期通信が終わった後にres.json()を実行
  • resjsonが終わったら次の処理を実行する

then()

then() メソッドは Promise を返します。最大2つの引数、 Promise が成功した場合と失敗した場合のコールバック関数を取ります。

※参考:Promise.prototype.then() - JavaScript | MDN

fetch(url).then(function(res) { ・・・①②
  return res.json(); ・・・③
}).then(function(json) { ・・・④
  //処理 json.xxxx〜
});

①fetch()メソッドを実行。引数はAPIのURL

②then()メソッドで次の処理をつなぐ。引数は無名関数でその引数はレスポンスのres

③urlのレスポンスresのjson()メソッドを実行し、結果(jsonデータ)をreturnで返す

④then()で次の処理を繋ぐ。引数は無名関数でその引数はjson

所感

一気に詰め込みすぎて脳内のメモリがショートしそう。

脳みそをオンプレミスじゃなくてクラウドに置きたい。

WindowOrWorkerGlobalScope.fetch() - Web API | MDN

【Vue.js】Vue CLIでaxiosを使う方法 - Qiita

axios を利用した API の使用 - Vue.js