20200602のvue.jsに関する記事は11件です。

hoverすると画像が100倍強調される超動きのある画像一覧をコピペだけで作成する方法

スクリーンショット 2020-06-02 17.23.10.png

Vueバージョン確認

npm list vue

まずは上記コマンドでバージョンの確認

twinzlabo@0.1.0 /Users/twinzlabo

── vue@2.6.11

BootstrapVueの導入

BootstrapVueの導入がまだの方のために念のため導入方法書いときますね

とりあえずコピペして環境を整えてください

main.js
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue)
npm install vue bootstrap-vue bootstrap

以上でBootstrapVueの導入は完了です

では次はお待ちかねの実装です!

hoverすると画像が100倍強調される超動きのある画像一覧をコピペだけで作成

すでに上の方で確認してもらったかと思いますが、

何の変哲もないBootstrapVueのイメージコンポーネントにスタイル修正を行うことで

そのうちの1つの画像をhoverすると他の画像が透過し選択したイメージだけが宙に浮いたように超強調される

超クールなアニメーション実装をしていきましょう
スクリーンショット 2020-06-02 0.49.32.png

デフォルトの上のイメージコンポーネントをhoverしたら下の画像のように
スクリーンショット 2020-06-02 0.49.59.png

hoverした画像だけが浮いているように強調されます

この感じプロが作ったようにクールですよね

でもこれがコピペだけで作れるんです

実装してみたら感動すること間違いなしです!

では早速コードをコピペしていきましょう

<template>
  <div class="do-animation pic-background">
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x800" fluid alt="Fluid image"></b-img>
    </div>
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x801" fluid alt="Fluid image"></b-img>
    </div>
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x802" fluid alt="Fluid image"></b-img>
    </div>
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x803" fluid alt="Fluid image"></b-img>
    </div>
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x804" fluid alt="Fluid image"></b-img>
    </div>
    <div class="pic">
      <b-img src="https://source.unsplash.com/1000x805" fluid alt="Fluid image"></b-img>
    </div>
  </div>
</template>
<style>
.do-animation {
  visibility: hidden;
}

.do-animation > * {
  visibility: visible;
}

.do-animation > * {
  transition: opacity 150ms linear 100ms, transform 150ms ease-in-out 100ms;
}

.do-animation:hover > * {
  opacity: 0.4; transform: scale(0.9);
}

.do-animation > *:hover {
  opacity: 1; transform: scale(1); transition-delay: 0ms, 0ms;
}

.pic-background{
  align-items:center;
  justify-content:center;
  margin-left: 55px;
  margin-top: 100px;
  width:100%;
}

.pic img {
  width:100%;
  height: 100%;
}

.pic{
  float: left;
  margin:0%;
  background:white;
  width:29%;
  height:250px;
  border:1px solid #d6d6d6;
  box-shadow:0 2px 3px 0px rgba(0,0,0,0.25);
  transition:.2s all;
}
</style>

いかがでしたでしょうか?

BootstrapVueのイメージコンポーネントにスタイル修正を行うことで

そのうちの1つの画像をhoverすると他の画像が透過し選択したイメージだけが宙に浮いたように超強調される

超クールなアニメーション実装ができましたか?

あんなに個性のカケラも感じなかったBootstrapVueのイメージコンポーネントが

いとも簡単にプロが作ったようなデザインに一変しちゃいましたね!(自画自賛)

こういうの実装できるとめっちゃ興奮しますよね

こちらに他にも面白いアニメーション実装記事があるので参考までに

下の記事ではより応用的で面白いCSSアニメーションの作成方法を掲載しているので是非挑戦してみてください

以上です

より応用的で面白いCSSアニメーションの作成方法学びたい方にはこちらの記事がおすすめです
現役フロントエンドエンジニアがおすすめするコピペで使えるアニメーション記事まとめ12選
スクリーンショット 2020-06-02 17.23.10.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とりあえずVue.jsのComposition APIで書くためのまとめ

自分含め、これまで Vue.js 2.x でコードを書いていた人に向けて書きます。
あまり深いところや細かいところまでは掘らずに、Vue.js 2.x で書いてきた基本的な記述をComposition APIで書くために、と言う視点で書いてみました。

目次

  • Composition API
  • Vue.js 2.xで慣れ親しんだ書き方からの移行
  • ロジックの抽出と再利用

Composition API

これまでのVue.jsでは data computed methods などのoptionごとにリアクティブなデータや関数などを書いてきたが(Options APIというらしい)、大きなコンポーネントになってくると必要な機能ごとにコードをまとめた方がいい。そのための関数ベースのAPI群。

Vue.jsのv3から利用可能予定。
v2でもプラグインとして利用可能。

現在はRFCとして仕様を揉んでいるところなのでここに記載の内容は変更される可能性があります。
https://vue-composition-api-rfc.netlify.app/

メリット

  • 自由なコードの構成
  • ロジックの抽出と再利用
  • 型推論の強化

これまでのVue.jsのOptions APIを上書きするのものではなく、上記のメリットを受けられるような大規模なプロジェクトで使うのが良い。
Options APIはそれ自体がコードの規約となるので小・中規模なプロジェクトでとても有用と思う。

setup()

setupは新しいOptionで、コンポーネント内でComposition API を使うときのエントリーポイントになります。
setup関数内にリアクティブなデータや算出プロパティ、メソッドなどを定義していきます。

<template>
 ...
</template>

<script>
import { computed, ref, reactive } from 'vue' // 必要なAPIはimportが必要
export default {
  setup() {
    // 
  }
}
</script>

<style>
 ...
</style>

setup関数内で使うcomputed, ref, reactive などの関数(後述)はimportしておく必要があります。

import { computed, ref, reactive } from 'vue'

templateで使うものは return { } が必要

templateで使う値や関数はsetup関数の最後でreturnしておく必要がある。
下の例でいうとreturnされた count などは、いままでのOptions APIで言うところの this.count のような形でぶら下ります。
いちいちこれを書く手間がある一方、templateでどの値/関数が使われているかがわかりやすいというメリットもあります。

<template>
  <div>
    {{ count }} * 2 = {{ double }}
    <button @click="increment">+</button>
  </div>
</template>

<script>
import { computed, ref, reactive } from 'vue'
export default {
  setup() {
    const count = ref(0)
    const double = computed(() => {
      return count.value * 2
    }

    const increment = () {
      count.value++
    }

    // template で出力するデータはここで渡す必要がある
    return { count, double, increment }
  }
}
</script>

Options APIからCompositio APIへの移行

これまで慣れ親しんだOptions APIでの書き方をComposition APIで書くときのまとめです。
datacomputedなどoptionごとに書いてます。

data

ref()、またはreactive()でリアクティブなデータを扱うことができます。

ref()

ref関数は数値や文字列、booleanなどのプリミティブ値を引数にとり、リアクティブなrefオブジェクトを返す
setup関数内でリアクティブな値を使う場合は.valueプロパティを参照します。

<template>
  <div>
    {{ count }}
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  setup() {
    const count = ref(0) // リアクティブな値をもつオブジェクトを返す
    console.log(count.value) // 0

    count.value++
    console.log(count.value) // 1

    const increment = () => {
      count.value++
    }
    return { count } // return する場合はリアクティブなrefオブジェクトごと返す。 .value は不要
  }
}
</script>

reactive()

オブジェクトを引数にとり、リアクティブプロキシーを返します。Vue.js 2.x系のVue.observable()と等しいようです。
(参考:Proxy - MDN web docs)

The reactive conversion is "deep": it affects all nested properties. In the ES2015 Proxy based implementation, the returned proxy is not equal to the original object. It is recommended to work exclusively with the reactive proxy and avoid relying on the original object.

<template>
  <div>
    {{ state.count }}
    {{ state.code }}
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      count: 1,
      code: 'ready'
    }

    state.count++ // 2 refオブジェクトと違い.valueを参照しない

    return { state }
  }
}
</script>

computed

算出プロパティはcomputed関数を使います。
引数にgetter関数をとり、イミュータブルなrefオブジェクトを返します。
templateで使う場合はもちろんreturnする必要があります。

import { ref, computed } from 'vue'
export default {
  setup() {
    const count = ref(2) 
    count.value++ // 3

    const double = computed(() => {
      return count.value * 2
    })
    console.log(double.value) // 6
    double.value++ // error

    return { double }
  }
}

methods

Composition APIのなかでmethodsは単純なJavaScriptの関数として宣言します。
template内で利用する場合はリアクティブな値と同様、setup関数の最後にreturnしてあげる必要があります。

<template>
  <div>
    Count is {{ count }} 
    <button @click="increment">+</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  const count = ref(1)

  // 単純なJSの関数として宣言する
  const increment = () => {
    count.value++
  }

  return { count, increment }
}
</script>

props

これまで同様、setup関数の外で指定。setup(props)のように引数に指定し使う。setupに渡されたpropsオブジェクトはリアクティブであり、

export default {
  props: {
    id: string
  },
  setup(props) {
    // props.id
  }
}

ただしpropsオブジェクトからプロパティだけを取り出すとリアクティビティは失われます。

export default {
  props: {
    id: Number
  },
  setup({ id }) {
    watchEffect(() => {
    console.log(`id is: ` + id) // リアクティブではなくなる。
    })
  }
}

created(), mounted(), updated()

RFCのリファレンスには以下のような対応表があります。
https://vue-composition-api-rfc.netlify.app/api.html#lifecycle-hooks

  • beforeCreate -> setup()を使う
  • created -> setup()を使う
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

created()

setup関数はpropの初期値が解決された後、beforeCreatedフックとcreatedフックの前に呼ばれます。
そのためsetup自体をbeforeCreated, createdとして扱うのがいいようです。

なお、setupはいままでのcreatedよりも前に解決されるため、

  • setup関数内からはこれまでの data() のプロパティやmethodsの関数にアクセスできない
  • 逆にsetupでreturn された値/関数はcreated() やmethodsの中で this.xxxの表記でアクセスできる。

となります。

mounted(), updated()

created, beforeCreated以外のライフサイクルフック、例えばmounted, updatedなんかは
onXxxxの形でimportした上でsetup関数内でonMounted() onUpdated() のように使います。

import { onMounted, onUpdated } from 'vue'
export default {
  setup() {
    onMounted(() => {
      console.log('mounted.')
    }
    onUpdated(() => {
      console.log('updated.')
    })
  }
}

そのほかのライフサイクルフック

beforeMount(), beforeUpdate(), beforeDestroy(), destroyed(), errorCaptured()などのそのほかのLife Cycle Hookも同様に関数をimportしてsetup内で使います。

import { onBeforeMount, onBeforeUpdate, onBeforeUnmount, onUnmounted, onErrorCaptured   } from 'vue'
export default {
  setup() {
    onBeforeMount(() => { ... }) // = beforeMount()
    onBeforeUpdate(() => { ... }) // = beforeUpdate()
    onBeforeUnmount(() => { ... }) // = beforeDestroy()
    onUnmounted(() => { ... }) // = destroyed()
    onErrorCaptured(() => { ... }) // = errorCaptured()
  }
}

emit, attrs, slots

emit, attrs, slotsは、setup関数の第二引数に渡されるcontextオブジェクトを通してアクセス可能です。

export default {
  setup(props, context) {
    context.attrs
    context.slots
    context.emit
  }
}

ロジックの抽出と再利用

Composition APIを使うメリットの一つにロジックの抽出とその再利用があります。

いくつかのコンポーネント間で利用されるような機能のロジックや、膨大で複雑なコードの機能を切り出して再利用することができ、見通しがよく効率的なコードが書けます。
ロジックを抽出して再利用する際にも、Composition APIのコード構成に対する非常に高い柔軟性が活きてきます。

以下の例はクリックで増えるカウントに対する機能を抽出しています。

// count.vue 
import { ref, computed, onMounted } from 'vue'

export function count() {
  const count = ref(0)

  const update = (e) => {
    count.value++
  }

  const displayCount = computed(() => {
    return status.value = count.value >= 10 ? '10+' : count.value
  })

  onMounted(() => {
    window.addEventListener('click', update)
  })

  return { cont, displayCount }
}

抽出したcount関数を利用します

import { count } from './count'

export default {
  setup() {
    const { count, displayCount } = count()

    return { count, displayCount }
  }
}

終わりに

Compositio APIの基本的な書き方、そしてこれまでのVue.jsに慣れ親しんだ人からの目線での書き方をまとめてみました。

Composition APIにはここで紹介していないAPIがまだあります。
冒頭のメリットでも触れたように、自由なコード構成、型サポートの強化、ロジックの抽出など、特に大規模なプロジェクトでは非常に可能性を感じさせる新機能となることでしょう。

一方、RFCである現在(2020/06)はAPIの追加や破壊的変更がされる可能性があります。
そのため業務での利用はまだ難しいですが、
来るべきVue.js 3.0の正式リリースに備え今から概要を掴んでいくのは良いかなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsの基礎を学ぶ

Vue.jsの読み込み方法

一般的にはCDN(content delivery network)を利用して読み込むことが多い。

jsDeliverというオープンソースのCDNがあるのでそれを利用する。

vue.jsの公式でインストールと検索するとscriptタグが出てくるので、それをbodyタグの末尾に記載する。

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>

その際にバージョンを固定してあげるのがおすすめ。バージョンを上げる際も動作確認してから上げること。

開発バージョンと本番バージョン

開発バージョンは警告出力とデバッグモードがある代わりにスピードが遅く、本番バージョンは警告とデバッグができないがスピードが速いという特徴がある。用途によって使い分けるべし。

Vueインスタンスの作成

var app = new Vue({

})

これが基本。これで空のVueインスタンスが作成されたことになる。

データバインディング

データと描画を同期する仕組み。

ex)inputタグの値を変えると表示されるmessageの値が変わるなど。

オプションを書いてみる

vueではオプションをel: '#app'のようにハッシュ形式で書いていく。

例えば

<div id="app">

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app'
})

このような場合、vueのオプションを適用させる要素がappというidを持つタグであるということを指定している。

実際にデータバインディングの仕組みを作る/Hello Vue.js!の表示

var app = new Vue({
  //options
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  }
})

このようにアプリで使うプロパティはdata:の中にハッシュ形式で書いていく。
今回作ったmessageというプロパティをview側で読み込むことでデータバインディングを行うことができる。

読み込む際は

<div id="app">
  <p>
    {{ message }}
  </p>
</div>

このようにプロパティを{{}}で囲んであげる。

これをマスタッシュ構文。口髭構文とも呼ぶ。{{が口髭に見えるから。

注意として、プロパティを読み込むことができるのはあくまでもルートテンプレートの中だけである。

更にオプションを足してみる/配列

<div id="app">
  <p>
    {{ message }}
  </p>
  <p>
    {{ count }}
  </p>
  <p>
    {{ user.prefecture }}
  </p>
  <p>
    {{ colors[1] }}
  </p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
    count: 10,
    user: {
      lastName: 'Yokota',
      firstName: 'Daiki',
      prefecture: 'Tokyo'
    },
    colors: ['Red', 'Green', 'Blue']
  }
})

このように、Railsのuser.nameやRubyのcolors[1]のような感じでVueでもコードを書くことができる。

ディレクティブとは?

v-で始まる特別な属性のこと。

v-bind
v-if
v-for
など。

v-bind

属性のデータバインディングv-bindについて学ぼう。

マスタッシュ構文はテキスト内での使用しかできず、属性に指定することができない。

ここで、v-bindというディレクティブを使用する

<div id="app">
  <input type="text" v-bind:value="message" />
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
   message: 'Hello Vue.js!'
  }
})

このようにすることで属性にプロパティの値を指定することができる。

v-if

要素の表示、非表示を切り替えるときなどに使う。

<div id="app">
  <p v-if="toggle">
    Hello
  </p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    toggle: true
  }
})

このように書くことで、trueのときは値を表示、falseのときは非表示にすることができる。

v-show

要素の表示、非表示を切り替える
こちらはcssのdisplayプロパティをon/offすることで表示を切り替えることができる。

<div id="app">
  <p v-show="toggle">
    Hello
  </p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    toggle: true
  }
})

この場合、v-ifと違ってドムツリーの中のpタグが消えるわけでは無い。
v-ifはドムレベルで削除される。

要素を頻繁に切り替える場合、v-showにした方が低コストらしい。

v-for

オブジェクトの繰り返しに使う

配列の要素を一つずつ表示したい時などに使う。

<div id="app">
  <ol>
    <li v-for="color in colors">{{ color }}</li>
  </ol>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    colors: ['Red', 'Green', 'Blue']
  }
})

このように書く。
注目すべきは、liタグに書いてある
v-for="color in colors"である。

colorとcolorsの名前は任意だが、配列のプロパティを複数形、マスタッシュ構文の中に書く値を単数形にしておくと分かりやすい。

v-for②/userオブジェクトのプロパティを表示してみる

v-forの使い方として、keyを指定することができる

<div id="app">
  <ul>
    <li v-for="value in user">{{ value }}</li>
  </ul>
  <hr>
  <ul>
    <li v-for="(value, key) in user">
      {{ key }}: {{ value }}
    </li>
  </ul>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    user: {
      firstName: 'Daiki',
      lastName: 'Yokota',
      age: 24
    }
  }
})

このようにkeyとvalueを使って書くことで

firstName: Daiki

と表示することができる。

v-on

ボタンをクリックすると現在時刻が表示されるシステムを作ろう

<div id="app">
  <button v-on:click="onclick">
    Click!
  </button>
  <p>
    {{ now }}
  </p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    now: ''
  },
  methods: {
    onclick: function() {
      this.now = new Date().toLocaleString();
    }
  }
})

v-on:click=""はボタンがクリックされた時に〜〜の処理をjs側で行いますよーって定義している。

htmlのonclickとはボタンが押された時にjs側で呼び出されるメソッド名にあたる。

js側では、methodsオプションを追加し、
onclick:以下に処理を書いていく。

自身のnowというプロパティにアクセスする際はthis.nowという風に書く。

v-model/双方向データバインディング

双方向バインディングとは、

dataオブジェクトの値変更→テンプレートの値変更

と、

テンプレートの値変更→dataオブジェクトの値変更

どちらも行うことができる。

<div id="app">
  <p>
   <input type="text" v-model="message">
  </p>
  <p>
   <input type="text" v-model="message">
  </p>
  <pre>{{ $data }}</pre>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  }
})

v-modelを使うことで双方向バインディングを実現している。

ポイントは、
1.1つ目のinputの値が変わる
2.messageプロパティの値が変わる(双方向バインディング)
3.2つ目のinputの値が変わる

という順番で動作が起こっていることを覚えておこう。

コンポーネント

コンポーネントとは、ページを構成するUI部品。
よく使う機能をコンポーネント化し、再利用性を高めることができる。

Hello!と表示するUIパーツを作成して使い回す

<div id="app">
  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
Vue.component('hello-component', {
  template: '<p>Hello</p>'
})
var app = new Vue({
  //options
  el: '#app',
})

構文は以下である。

Vue.component('component名', {
  template: '表示したい要素ex)<p>hello</p>
})

これで、view側で、とすることでjs側で指定した要素を表示することができる。

ToDoアプリを作る

最終的なコード

<div id="app">
  <h2>TODO List</h2>
  <form v-on:submit.prevent>
    <input type="text" v-model="newItem">
    <button v-on:click="addItem">
      Add
    </button>
  </form>
  <ul>
    <li v-for="(todo, index) in todos">
     <input type="checkbox" v-model="todo.isDone">
      <span v-bind:class="{ done: todo.isDone }">
        {{ todo.item }}
      </span>
      <button v-on:click="deleteItem(index)">Delete</button>
    </li>
  </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
#app ul {
  list-style: none;
}

#app li > span.done {
  text-decoration: line-through;
}
var app = new Vue({
  //options
  el: '#app',
  data: {
    newItem: '',
    todos: []
  },
  methods: {
    addItem: function(event) {
      //タスクが未入力の場合は配列にタスクを追加しない
      if(this.newItem == '') return;
      var todo = {
        item: this.newItem,
        //タスクの完了・未完了
        isDone: false
      }
      //配列にタスクを追加する
      this.todos.push(todo)
      //入力欄を空欄にする
      this.newItem = ''
    },
    deleteItem: function(index) {
      //タスクを削除するspliceメソッド
      this.todos.splice(index, 1)
    }
  }
})

注意点はindex。
これは、配列なのでkeyにはindex番号は入るということ。

spliceメソッドは第一引数に削除する要素のindex番号、2つ目に削除する長さを指定することができる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】Nuxt文法編:v-for

前置き

文法シリーズ第二弾
今回は定番のv-forです!

文法は覚えましょう?
ある程度の構文・単語は
覚える必要があります?
英文法・英単語を覚えないと
文が作れないのと同じです??

❓どんな時に使うか
配列やオブジェクトを
listで描写させる時に使います。
https://note.com/aliz/n/nda7438249ca8

v-for

https://qrunch.net/@rokujiro/entries/ylmWdFAZsaAtMTpq?ref=qrunch

【解説】
・v-for="i in 6"
 forといえば繰り返しですね。
 数字の6までの1つ1つをiとし
 それを繰り返しています。
・:key
 ここだと分かりにくいので
 後半に解説します。

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="i in 6"
       :key="i"
     >
       {{ i }}
     </li>
   </ul>
 </div>
</template>

【表示】
スクリーンショット 2020-06-01 13.21.56.png

配列の繰り返し

【解説】
・v-for="item in items"
 配列のitemsにある
 1つ1つのオブジェクトをitemと名付けます。
 in の代わりに of も使用できます?
・:key (v-bind: key)
 プロパティ= key
 1つ1つの要素がitemなので
 item.messageになります?
 (itemの中のmessageプロパティ)
 ⏬左がプロパティ: 右が値です!
 message: 'Foo',
 message: 'Bar',
 v-forには基本的に一意のkeyを指定します。
 要素を効率よく追跡し、
 パフォーマンス向上のためです。
 一意については
 オブジェクトのv-forで解説しています?
 ?keyによる再利用可能な要素の制御
  https://note.com/aliz/n/n76280a0e3a02/edit
・{{ item.message }}
 itemsの1つ1つの要素をitemとしたので
 itemのmessageプロパティの値を表示
Frame 1.png

index.vue
<template>
 <div class="page">
   <ul>
     <li<img width="665" alt="スクリーンショット 2020-06-01 10.48.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/3923d8d8-bbcd-4d8e-309d-5673130b1fea.png">

       v-for="item in items"
       :key="item.message"
     >
       {{ item.message }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     items: [
       { message: 'Foo' },
       { message: 'Bar' },
     ]
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 10.48.48.png

配列番号のサポート

v-forは配列[ ]のインデックス(配列番号)を
第二引数としてサポートしています?
配列番号は0からスタートします!

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(item, index) in items"
       :key="item.message"
     >
       {{ parentMessage }} - {{ index }} - {{ item.message }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     parentMessage: 'Parent',
     items: [
       { message: 'Foo' },
       { message: 'Bar' },
     ]
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.05.07.png

【解説】

v-forで繰り返すのはitem.messageのみ
そのため配列0,1の2つが表示されます。

オブジェクトのv-for

配列だけでなくオブジェクトにも使えます?

index.vue
<template>
 <div class="page">
   <ul>
     <li v-for="value in object">
       {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】

スクリーンショット 2020-06-01 13.31.31.png

?オブジェクトでも配列と同様に
 keyと配列番号が使用できます!

【index.vue: keyを使用】
バラバラのプロパティ名ですが、
これらのkeyをnameと名付けて使用しています?

index.vue
<template>
 <div class="page">
   <ul>
     <li
      v-for="(value, name) in object"
      :key="name"
     >
       {{ name }} : {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.36.37.png

【index.vue: 配列番号indexを使用】

index.vue
<template>
 <div class="page">
   <ul>
     <li
      v-for="(value, name, index) in object"
      :key="name"
     >
       {{ index }}. {{ name }} : {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.44.42.png

:keyに使う値

❌非プリミティブ値
 オブジェクトや配列
⭕️プリミティブ値
 文字列や数字

【解説】
・:key="index"
 indexは配列番号のため非プリミティブ値
 配列は追加したり削除したりで
 対の値がズレることがあるので
 予期せぬ挙動になることがあります?

【❌index.vue: NG例】

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(todo, index) in todos"
       :key="index"
       @click="deleteTodo(index)"
     >
       {{ todo.value }}
     </li>
   </ul>
 </div>
</template>

<script>
import Vue from 'vue'

export default {
 data () {
   return {
     todos: [
       { id: 1, value: '記事を書く' },
       { id: 2, value: '投稿する' },
       { id: 3, value: 'ツイートする' },
     ]
   }
 },
 methods: {
   deleteTodo (index) {
     this.$delete(this.todos, index)
   },
 },
}
</script>

【解説】
・:key="todo.id"
 idは文字列のためプリミティブ値
 todos配列の中のオブジェクト1つ1つがtodo
 そのtodoにあるプロパティidと
 valueを含めてidと名付けています?

【補足】
・$delete
 VueのグローバルAPI
 Vue.deleteを使用しています!
 そのためvueのimportが必要です?
 Nuxtで使う際には$deleteと書きます✍️

【⭕️index.vue: OK例】

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(todo, index) in todos"
       :key="todo.id"
       @click="deleteTodo(index)"
     >
       {{ todo.value }}
     </li>
   </ul>
 </div>
</template>

<script>
import Vue from 'vue'

export default {
 data () {
   return {
     todos: [
       { id: 1, value: '記事を書く' },
       { id: 2, value: '投稿する' },
       { id: 3, value: 'ツイートする' },
     ]
   }
 },
 methods: {
   deleteTodo (index) {
     this.$delete(this.todos, index)
   },
 },
}
</script>

記事が公開したときにわかる様、
フォローをお願いします??

https://twitter.com/aLizlab

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】Nuxt文法編:v-if, v-else, v-else-if

前置き

今までの記事で
何度も使ってきたv-if!
記事ごとに解説はしていましたが
v-if自体の記事はない??
ということが、ちらほら。。。
そこで文法の記事を投稿していきます??
今後v-forやv-modelなどを
更新していくのでお楽しみに♪

❓どんな時に使うか
表示/非表示の切り替えに使います!
buttonをclickしてモーダルなどの
表示/非表示を切り替えたりします。
https://note.com/aliz/n/n2f0bc857defb

v-if

ディレクティブの式が
真の時のみブロックが描写されます!
単一の要素にv-ifをつけます。

index.vue
<template>
 <div class="page">
   // h1タグの描写をtitleの真偽値で切り替え
   <h1 v-if="title">
    titleがtrueの時のみ表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     // trueなので描写
     title: true,
   }
 },
}
</script>

【表示】

?true?
h1タグが描写されます。
スクリーンショット 2020-05-26 16.50.15.png

?false?
title: falseにした場合
h1タグが描写されません。
スクリーンショット 2020-05-26 16.50.59.png

? ! を使うと…
 真偽値を逆にすることができます!

index.vue
<template>
 <div class="page">
   // titleがfalseですが!で逆にするのでtrueになり描写されます
   <h1 v-if="!title">
    titleがtrueの時のみ表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     title: false,
   }
 },
}
</script>

?複数の要素を
 切り替えたい場合

 →親要素にv-ifをつけてあげます?

index.vue
<template>
 <div class="page">
   <p>親要素ごと描写が切り替わる</p>
   <div v-if="titles">
     <h1>
       Hello Nuxt.js!
     </h1>
     <h2>
       Hello Nuxt.js!
     </h2>
     <h3>
       Hello Nuxt.js!
     </h3>
   </div>
 </div>
</template>

<script>
export default {
 data () {
   return {
     titles: false,
   }
 },
}
</script>

【表示】
スクリーンショット 2020-05-26 17.17.01.png

v-else

v-if="true":v-ifのみ描写
v-if="false":v-elseのみ描写

v-ifとセットで使います!
v-elseはv-ifもしくはv-else-ifの
直後に書きましょう!
それ以外は認識されません??

index.vue
<template>
 <div class="page">
   <h1 v-if="title">
     Hello Nuxt.js!
   </h1>
   // titleがfalseのためv-elseが描写
   <h1 v-else>
     v-ifがfalseの場合に表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     title: false,
   }
 },
}
</script>

?v-ifに直接関数を入れてみましょう
 Math.random()で
 0〜1の数字をランダムで作り
 0.5より上ならv-ifがtrueとなります。
 より、なので0.5は含みません。
 数字は確認したいので
 Mustache構文(二重中括弧)で
 表示させておきます?

index.vue
<template>
 <div class="page">
   {{ Math.random() }}
   <h1 v-if="Math.random() > 0.5">
     Hello Nuxt.js!
   </h1>
   <h1 v-else>
     v-ifがfalseの場合に表示
   </h1>
 </div>
</template>

<script>
export default {
}
</script>

【表示】

?true?
スクリーンショット 2020-05-26 17.45.52.png

?false?
スクリーンショット 2020-05-26 17.44.58.png

v-else-if

複数の条件を指定したい時に使います?
こちらもv-ifもしくはv-else-ifの
直後に書きましょう!

index.vue
<template>
 <div class="page">
   <h1 v-if="type === 'A'">
     A
   </h1>
   <h1 v-else-if="type === 'B'">
     B
   </h1>
   <h1 v-else-if="type === 'C'">
     C
   </h1>
   <p v-else>
     ABCどれにも当てはまりません
   </p>
 </div>
</template>

<script>
export default {
 data () {
   return {
     type: 'E'
   }
 },
}
</script>

keyによる再利用可能な要素の制御

効率的に描写するために
要素を再利用することがよくあります。
例えばこちらのコード。
v-ifとv-elseでinputを分けています?
toggleボタンでinputを切り替えます?

index.vue
<template>
 <div class="page">
   <label v-if="loginType === 'username'">
     Username
     <input placeholder="Enter your username">
   </label>
   <label v-else>
     Email
     <input placeholder="Enter your email address">
   </label>
   <button
     @click="toggle"
   >
     toggle
   </button>
 </div>
</template>

<script>
export default {
 data () {
   return {
     loginType: 'username'
   }
 },
 methods: {
   toggle () {
     if (this.loginType === 'username') {
       this.loginType = 'email'
     } else {
       this.loginType = 'username'
     }
   },
 },
}
</script>

【表示】
toggleを押しても
inputに入力したものが残ってます。
切り替えができていません。
placeholderは変わっていますが、
input自体が再利用されています?
この場合の再利用は
望ましくありません?
input.gif

【key属性】
そこでkey属性の出番!
Vueで使える特別な属性のことです?
これを追加することで
「別物だよ!再利用しないでね!」
と伝えることができます?

index.vue
<template>
 <div class="page">
   <label
     v-if="loginType === 'username'"
   >Username
     <input
       placeholder="Enter your username"
       // inputkey属性を追加
       key="username-input"
     >
   </label>
   <label v-else>Email
     <input
       placeholder="Enter your email address"
       // inputkey属性を追加
       key="email-input"
     >
   </label>
   <button
     @click="toggle"
   >
     toggle
   </button>
 </div>
</template>

<script>
export default {
 data () {
   return {
     loginType: 'username'
   }
 },
 methods: {
   toggle () {
     if (this.loginType === 'username') {
       this.loginType = 'email'
     } else {
       this.loginType = 'username'
     }
   },
 },
}
</script>

【表示】
input2.gif

inputが最初から
描写されていますね✨?
※ inputはkey属性を持ちますが
 labelにはないため
 labelは再描写されています!

記事が公開したときにわかる様、
フォローをお願いします??

https://twitter.com/aLizlab

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

現役フロントエンドエンジニアがガチでおすすめするコピペで使えるCSSアニメーション記事まとめ12選

スクリーンショット 2020-06-02 17.23.10.png

こちらの記事に記載のコードは全て自由に使っていただいて大丈夫なので
どんどんあなたのプロジェクトに取り込んでより充実したデザインにしてください
*動きだけ確認したい初学者の方はJSFiddle使ってみるといいですよ

それでは早速コピペで使えるCSSアニメーションの紹介をしていきましょう!
(ランキング形式ではありません)


1 hoverすると画像が100倍強調される超動きのある画像一覧をコピペだけで作成
スクリーンショット 2020-06-02 0.49.59.png

2 ダサいイメージコンポーネントでプロのような画像一覧を作成する方法
スクリーンショット 2020-06-02 0.29.38.png

3 まるで車窓のような画像一覧にhoverのCSSアニメーションをつけてみる
スクリーンショット 2020-06-01 19.58.02.png

4 個性のないBootstrapVueカードコンポーネントにクールなhoverアニメーションを実装
スクリーンショット 2020-06-01 21.31.56.png

5 ナビゲーションバーをhoverすると他のタブがぼやけるアニメーション実装
スクリーンショット 2020-06-01 19.05.24.png

6 hoverで動きのあるオシャレな画像一覧をBootstrapVueイメージコンポーネントの応用で実装
スクリーンショット 2020-06-01 22.53.31.png

7 画像をhoverすると全体像がスーッと現れるアニメーション実装
スクリーンショット 2020-05-31 21.55.38.png

8 画像をhoverすると背景が段階的に上から降ってきて文字を表示する実装
スクリーンショット 2020-05-31 15.52.25.png

9 画像をhoverするとその画像がスーッと拡大されるアニメーションをコピペだけで実装
スクリーンショット 2020-05-31 21.36.02.png

10 画像をhoverすると右から階段のようにスーッと背景と文字が現れる実装をコピペだけ
スクリーンショット 2020-05-31 15.36.45.png

11 画像をhoverすると自動ドアのようにスーッと背景+descriptionが現れるCSSアニメーション実装
スクリーンショット 2020-05-30 20.47.03.png

12 白黒画像をhoverすると色鮮やかな画像に変化するクールなアニメーション実装
スクリーンショット 2020-05-30 17.31.13.png


いかがでしたか?
プロジェクトで使えるかは置いといても結構楽しんでもらえたかと思います

以上です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Jest で Vue.js を単体テストしよう

vue create で生成したプロジェクトで typescript と Jest を選択しているプロジェクトで単体テストをしてみましょう。といっても既に tests/unit/example.spec.ts に以下のようなコードが生成されています。

import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})

お膳立てが出来ているって素晴らしい。

テスト用コンポーネントの作成

ということで、ちょっと頑張って以下のようなコンポーネントを作ってみました。

<template>
  <div>
    <div v-if="isEmpty">
      データがありません
    </div>
    <table class="content" v-else>
      <thead>
        <tr>
          <th>Date</th>
          <th>Temperature C</th>
          <th>Summary</th>
        </tr>
      </thead>
      <tbody>
        <tr :key="index" v-for="(f, index) in store.forecasts">
          <td>{{ f.date }}</td>
          <td>{{ f.temperatureC }}</td>
          <td>{{ f.summary }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import store from '@/store';
export default Vue.extend({
  data() {
    return {
      store,
    };
  },
  async created() {
    await store.loadWeatherForecasts();
  },
  computed: {
    isEmpty(): boolean {
      return this.store.forecasts.length == 0
    },
  },
});
</script>
<style>
table.content {
  margin-right: auto;
  margin-left: auto;
}
</style>

ポイントは、このコンポーネントは store という得たいの知れない別ファイルに依存していて、そいつはどうも loadWeatherForecasts という非同期メソッドを created で呼んでいるみたいだということです。

おそらく、そのメソッドの呼び出しが終わると store.forecasts の配列にデータが入っていて、それを元にテーブルを描画してるんだなというのがわかります。データが空のときはデータが無いという旨のメッセージを出していますね。

実際のコードでは loadWeatherForecasts は Swagger から生成した TypeScript 用の API のクライアントコードを使ってます。forecasts は、その戻り値の型の配列です。

じゃぁテスト作ってみましょう。

テスト作成

shallowMount で Forecast.vue で定義されたコンポーネントをマウントします。shallow という名前の通り子コンポーネントは純粋にスタブになっている点が単体テスト向きです。一応 shallow じゃない mount も用意されてますが、子コンポーネントの描画も含めてテストしたいときは使う感じですが、単体テストとしてなら shallowMount のほうが適切な感じですね。

さくっとやってみましょう。モックの差し込み方とかは 1 つ前の記事でやった方法で OK です。

TypeScript の単体テストで Jest 使おう

import { shallowMount } from '@vue/test-utils';
import { api } from '@/api';
import store from '@/store';
import Forecasts from '@/views/Forecasts.vue';

jest.mock('@/store');

const mockStore = store as jest.Mocked<typeof store>;

describe('Forecasts.vue', () => {
    test('データが空のときはテーブルを描画しない', () => {
        mockStore.forecasts = [];
        const forecasts = shallowMount(Forecasts);

        expect(mockStore.loadWeatherForecasts).toHaveBeenCalled();
        expect(forecasts.text()).toMatch('データがありません');
        expect(forecasts.find('table').exists()).toBe(false);
    });

    test('データがあるときはテーブルが描画される', async () => {
        mockStore.loadWeatherForecasts.mockImplementation(() => {
            mockStore.forecasts = [ 
                new api.WeatherForecast({ date: new Date(2020, 5, 1, 0, 0, 0), temperatureC: 0, temperatureF: 10, summary: 's1' }),
                new api.WeatherForecast({ date: new Date(2020, 5, 2, 0, 0, 0), temperatureC: 1, temperatureF: 20, summary: 's2' }),
                new api.WeatherForecast({ date: new Date(2020, 5, 3, 0, 0, 0), temperatureC: 2, temperatureF: 30, summary: 's3' }),
            ];
            return Promise.resolve();
        });

        const forecasts = shallowMount(Forecasts);
        const tableRows = forecasts.findAll('table > tbody > tr');

        expect(mockStore.loadWeatherForecasts).toHaveBeenCalled();
        expect(tableRows.length).toBe(3);
        expect(tableRows.at(0).text()).toBe('Mon Jun 01 2020 00:00:00 GMT+0900 (GMT+09:00) 0 s1');
        expect(tableRows.at(1).text()).toBe('Tue Jun 02 2020 00:00:00 GMT+0900 (GMT+09:00) 1 s2');
        expect(tableRows.at(2).text()).toBe('Wed Jun 03 2020 00:00:00 GMT+0900 (GMT+09:00) 2 s3');
    });
});

さくっとグリーンになりました。JavaScript 系のテストはモックを差し込みやすいポイントとしては import になるので、きちんとファイル分割して import するように作ってれば単体テストのときにつぶしが効きそうですね。

その他知っておいた方がよさそうなやつ

正直公式のガイドが凄くよく書かれています。しかも日本語で(翻訳してくれている人に大感謝)

Vue Test Utils | ガイド

createLocalVue 関数

テスト用の Vue インスタンスを作ってくれます。

// ローカルの Vue インスタンス
const localVue = createLocalVue();
// 色々設定(ここでは VueRouter)
localVue.use(VueRouter);
const router = new VueRouter();

// ローカルの Vue インスタンスを使ってマウントする
const testTarget = shallowMount(SomeComponent, {
  localVue,
  router,
});

Vue インスタンスは何も考えないとグローバルで 1 つなので、別のテストで汚染された Vue インスタンスではなく、個々のテストで Vue インスタンスを作るってことですかね。

Vuex のテスト

Vuex も基本的にはモックをうまく使ってテストです。ガイドに書いてある通りでよさそう。

非同期系

ガイドで紹介されている flush-promises がとっても便利そう。全ての resolve されている Promise のハンドラーを実行してくれる。

使い方も await flushPromises(); をするだけでシンプル。便利そう。

ボタンを押したりしたい

trigger メソッドをガイドで探せば OK。

イベントの発行の確認

shallowMount の戻り値に対して emitted() を呼ぶと結果が返ってくるので、これをアサートすれば OK

まとめ

公式で日本語で書いてあるのマジ神。

まとめ

単体テストしよう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js]スクロールでコンテンツがフェードインする実装

はじめに

LPやコーポレートサイトでよく、スクロールするとコンテンツがフワッとフェードインするサイトが見られます。
本記事は、自分がNuxt.jsを使ってこの機能を実装した時に、ライブラリ、jQuery未使用で実装した内容を記事にします。
完成イメージをCodePenに載せているのでご参考にしてください。

See the Pen scroll-fade-in by Matsumoto (@RyoTa0222) on CodePen.

目次

  1. コンポーネントの作成
  2. v-ifとv-showの違い
  3. アニメーションを適用する
  4. 動作確認
  5. 最後に

1. コンポーネントの作成

Vue.jsで実装するため、機能が共通している部分をコンポーネントにします。今回では、「スクロールしていくとコンテンツがフェードインする」という部分が共通の機能となるため、この部分をコンポーネントにします。

<template>
    <div>
      <slot v-show="visible"></slot>
    </div>
</template>

<script>
  export default {
    data() {
      return {
        visible: false
      };
    },
    created() {
      window.addEventListener("scroll", this.handleScroll);
    },
    destroyed() {
      window.removeEventListener("scroll", this.handleScroll);
    },
    methods: {
      handleScroll() {
        if (!this.visible) {
          var top = this.$el.getBoundingClientRect().top;
          this.visible = top < window.innerHeight + 100;
        }
      }
    }
  }
</script>

テンプレートの部分では、slotでコンテンツの置換を行っています。変数のvisibleはデフォルトでfalseとなっており、非表示の状態になっています、このコンポーネントでは、スクロールイベントを設定し、デフォルトで非表示の要素が一定の高さにくるとvisibletrueになり、表示されます。
this.$el.getBoundingClientRect().top;では、ブラウザの表示領域の左上を(0, 0)として、そこから要素の上端までの相対位置の値が返ってきます。なので、this.visible = top < window.innerHeight + 100;では、ブラウザのウインドウの高さに100pxを足した値よりtopの値が小さくなった時に、this.visibletrueが代入されます。

次に、描画するかどうかをv-ifv-showのどちらで切替を行うかについてお話します。

2. v-ifとv-showの違い

v-ifはDOMへの描画の切替を行います。そのため、DeveloperToolsを用いるとその要素が描画されていないことがわかります。
一方で、v-showは、画面への表示、非表示の切替がCSSで行われます。つまりDOM上では常に描画された状態であり、例えばv-show="false"は、CSSでdisplay: none;が与えられて非表示になっています。

今回のケースでどちらを用いるのが最適かというと、いろいろなサイトを見てもらうとわかるのですが、ページの長さがコンテンツの表示、非表示に関係なく同様となっています。つまり、DOM上には常に描画された形であり、v-showによって切り替えるのが正しいです。

3. アニメーションを適用する

Vue.jsでアニメーションといえば、トランジションをまず頭に思い浮かべます(トランジションについて)。Vue.jsのドキュメントにも以下のように書いてあります。

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

  • 条件付きの描画 (v-if を使用)
  • 条件付きの表示 (v-show を利用)
  • 動的コンポーネント
  • コンポーネントルートノード (Component root nodes)

ただ、ここまで説明しておいて、今回はトランジションを使っておらず、CSSのanimationを使ってフェードインを実装しています:upside_down:
理由としては、自分がなかなかトランジションを使いこなせていないからです、、、そのうちトランジションマスターになってこの記事が更新されると信じています笑

<template>
  <div :class="{fadeIn: visible}">
    <slot v-show="visible"></slot>
  </div>
</template>

<style>
.fadeIn {
  animation: fadeIn 2s;
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    transform: translateY(100px);
  }
  100% {
    opacity: 1;
    transform: translateY(0px);
  }
}

ここでは、templateのdiv要素に対して、visibleがtrueとなった場合に動的にfadeInというクラス名が付与されています。
そして、CSSでfadeInという名前の@keyframesでアニメーションを作っています。

4. 動作確認

今回はコンポーネントの名前をFadeInComponentとしています。
このコンポーネントを呼び出す時は、<fade-in-component>タグの子要素に表示させたい中身を記述するだけです!

<fade-in-component>
   <section>
       <div class="wrapper">
          <h1>Title00</h1>
          <h2>subtitle00</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
          <h2>subtitle01</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </p>
          <div class="d-flex justify-space-around my-4">
            <v-btn depressed color="info">button</v-btn>
             <v-btn outlined color="info">button</v-btn>
          </div>
       </div>
   </section>
</fade-in-component>

動作確認の方は、冒頭のCodePenの方で確認してみてください!また、animation: fadeIn 2s;の秒数やthis.visible = top < window.innerHeight + 100;の部分を変えたりして、好みに調整してみてください!

5. 最後に

今回実装したものは一つの方法でしかありません。ライブラリやjQueryに依存せず、他にももっといい方法があるかもしれないので、発見出来次第、追記していきたいと思っています!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

firebase Cloud Messaging + Laravel + Vue.js、 PWAで web push 対応のリアルタイムな チャットを作る 作例編

以前の 製作事例公開内容となり、他ブログ等で公開済ですが。こちらにも記事残したいと思います
概要的な内容が多く、コード説明等が少ないので、紹介程度の記事となります

概要

firebase Cloud Messaging, Notification API 等で
web push通知対応のチャット機能の作成となります。

参考のコード / GitHub

https://github.com/kuc-arc-f/lara58a_6chat

構成

firebase Cloud Messaging / FCM
Progressive Web Apps / PWA
Notification API
Laravel 5.8
Vue.js
nginx
mysql

画面

PC版 / PWA チャット画面
ss-chat-webpush-receive.png

PC版 / web push 受信 : winタスクバーの通知画面と、通知音出ます。
ss-notify.png

android版 / PWA チャット画面
Screenshot_2020-06-02-09-37-54.jpg

FCM, PWAの、注意点など

・HTTPで使用できない。 設置環境はSSL化しておく必要あり
・ブラウザの通知設定を。許可に変更する必要あり

設計面での、注意点

・Cloud messageのサンプル等見ると、メンバー(自分以外) に、web pushする例が無かった気がしましたが。
web push送信時に、送信先トークンを指定する必要があり。管理方法含めて検討する必要がありました
・例として、DBに参加チャット 各メンバーのトークンを保存し。送信時に利用する事にしました。
 送信時には、送信者に 自分のトークンを含めない仕様にしています。

実装など

web push

function fcm_send(send_title, send_body, IID_TOKEN, FCM_SERVER_KEY){

    if(IID_TOKEN.length < 1){ return; }
    var key = FCM_SERVER_KEY;
    var to = IID_TOKEN;
    var notification = {
        'title': send_title,
        'body': send_body,
        'icon': 'firebase-logo.png',
        'click_action': 'http://hoge'
    };
    fetch('https://fcm.googleapis.com/fcm/send', {
    'method': 'POST',
    'headers': {
        'Authorization': 'key=' + key,
        'Content-Type': 'application/json'
    },
    'body': JSON.stringify({
        'notification': notification,
        'to': to,
        })
    }).then(function(response) {
    }).catch(function(error) {
        console.error(error);
    })
};

・web push 受信する
messaging.onMessage コールバックで、受信後の通知、再描画等

    messaging.onMessage((payload) => {
        var notify = payload.notification;
        recv_pushMessage("", notify.body);
        var data = {
            'id' : 0,
            'chat_id': CHAT_ID ,
            'user_id': 0,
            'body': notify.body,
            'created_at': null,
        };
        console.log(notify.body );
    });

・ Notification API で、通知

function recv_pushMessage(title, body){
    if (!('Notification' in window)) {//対応してない場合
        alert('未対応のブラウザです');
    }
    else {
        // 許可を求める
        Notification.requestPermission()
        .then((permission) => {
            if (permission === 'granted') {// 許可
                var options ={
                    body: body,
                    icon: 'https://hoge.net/icon.png',                                      
                    tag: ''
                };
                var n = new Notification(title,options);
                console.log(n);
                setTimeout(n.close.bind(n), 5000);
            }
            else if (permission == 'denied') {// 拒否
            }
            else if (permission == 'default') {// 無視
            }
        });
    }  
}

参考のページ

https://knaka0209.hatenablog.com/entry/lara58_13chat


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsでGatsbyJS的なことができるnuxt/contentを試してみる

GatsbyJSとは?

GatsbyJSとは、React製の静的サイトジェネレーターです。

  • UIはReactで構築
  • データは article.md のようなマークダウンで作る
  • ビルドすることで、トップページ・タグアーカイブ・記事個別ページなどなど必要なファイルを .html で生成する

こんなことができます。
静的HTMLファイルなので、Netlify / AWS Amplify Console / Firebase Hosting等にデプロイすれば非常に低コストで運用できます。
データベース等も使わないので、万が一急にアクセスが増えても、先述のようなホスティングサービスを利用していればまず問題なく捌けます。

以前ポートフォリオサイトとブログをGatsbyJSで作ってみましたが、データの取得にGraphQLを使う部分や、各ページを生成するロジックを実装するあたりでかなりてこずってしまい、「できればNuxt.jsでやりたいなー」と思っていました。

nuxt/contentが出た!

2020/5/24にnuxt/contentがバージョン1.0.0を迎えました!
https://github.com/nuxt/content

本家サイト( https://content.nuxtjs.org/ )も非常にシンプルでわかりやすく、簡単にサンプルを組み立てることができました。
内容としてはまさに欲しかったものです!

使ってみる

  • yarnをインストールする(省略)
  • Nuxt.jsプロジェクトを作成する
# プロジェクトを作成
$ yarn create nuxt-app nuxt-content-blog

# オプションは全てデフォルトにしました
? Project name nuxt-content-blog
? Project description My tiptop Nuxt.js project
? Author name tetsushi
? Choose programming language JavaScript
? Choose the package manager Yarn
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)

# ディレクトリを移動
$ cd nuxt-content-blog

# ローカルサーバーを起動
$ yarn dev

http://localhost:3000/ にアクセスすると以下のようないつもの画面が表示されます。

スクリーンショット 2020-06-02 08.42.46.png

  • nuxt/contentをインストールする

公式ドキュメントに沿って進めればハマるところはありませんでした。
https://content.nuxtjs.org/installation

$ yarn add @nuxt/content
  • nuxt.config.js を調整する
{
  modules: [
    '@nuxt/content' // これを追加
  ],
}

これであっさり準備完了です。
ディレクトリ構造はこのようになりました。

$ tree -I node_modules
.
├── README.md
├── assets
│   └── README.md
├── components
│   ├── Logo.vue
│   └── README.md
├── layouts
│   ├── README.md
│   └── default.vue
├── middleware
│   └── README.md
├── nuxt.config.js
├── package.json
├── pages
│   ├── README.md
│   └── index.vue
├── plugins
│   └── README.md
├── static
│   ├── README.md
│   └── favicon.ico
├── store
│   └── README.md
└── yarn.lock

記事ファイルを置いてみる

ブログを作るイメージで、記事を書いてみます。
content/articles というディレクトリを作り、 article-1.mdarticle-2.md を作成しました。

$ tree content/
content/
└── articles
    ├── article-1.md
    └── article-2.md

content/articles/article-1.md の内容は以下です。

content/articles/article-1.md
---
title: 記事1
tags: [Nuxt.js JavaScript]
---

# 見出し1

## 見出し2

本文本文

content/articles/article-2.md にも同じような内容を書きました。
これで記事は準備完了です。

一覧ページを作ってみる

ブログのトップページをイメージして、記事一覧を出力してみます。
トップページ(/)にページを作るので、 pages/index.vue を作成します。

pages/index.vue
<template>
  <div>
    <div v-for="article in articles">
      <nuxt-link :to="article.path">{{ article.title }}</nuxt-link>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  async asyncData({ $content }) {
    const articles = await $content("articles")
      .limit(5)
      .fetch();

    return {
      articles
    };
  }
};
</script>

asyncData 関数でクエリを組み立てます。
クエリはLokiJSというインメモリデータベースライブラリで組み立てます。
https://github.com/techfort/LokiJS

これだけのコードであっという間に素朴なトップページが出来上がってしまいました。

スクリーンショット 2020-06-02 08.54.10.png

リンク先は article.path としています。
content/articles/article-1.md というファイルを作ったので、デフォルトではこの投稿のパスは /articles/article-1 となります。

記事個別ページのテンプレートはまだないので、クリックしてもエラーになります。
では、次に記事個別ページを作ります。

記事個別ページを作る

記事個別ページは /articles/{slug} にします。
pages/articles/_slug.vue を作成しました。

pages/articles/_slug.vue
<template>
  <div class="container">
    {{ article.title }}

    <nuxt-content :document="article" />
  </div>
</template>

<script>
export default {
  components: {},
  async asyncData({ $content, params }) {
    const article = await $content(`articles/${params.slug}`).fetch();

    return {
      article
    };
  }
};
</script>

これで個別ページも完成です。

スクリーンショット 2020-06-02 08.57.42.png

まとめ

記事の配列を得る方法と、単一の記事を得る方法をまず調べました。
これさえあればあとはタグアーカイブを作ったり、ページネーションを作ったりすることはさほど苦労しないかと思います。

見た目を整えるのは普通のVue.js / Nuxt.jsの開発と同じようにコンポーネントを作ってやっていけそうです。

普段はAngularで開発していますが、React / VueのHMRは非常に便利です。
ブログ執筆等では、なんならWordPress等のCMSよりも書きやすいかもしれません。

コンテンツがDBではなくGitで管理できるのも魅力的です。
必要な実装方法を調べ終わったらどんどんGatsbyJSから乗り換えて行こうかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL(WSL2)でビルドが遅くて重くてホットリロードが効かないとき

結論

WSLを使うとき、/mnt/c配下で開発せず、~で開発しなさい。

概要

WSL(WSL2)上でVue(Nuxt)やReact(Next, Gatsby)などの開発をしていて、ビルドすると始まりが遅いしビルド時間もかなり長くなるという方や、ファイル名の変更ができなかったり、保存時にホットリロードが効いていないなどで悩んでいる方用の対処法。

なぜ?

  • WSLでのビルド時間が長くなってしまう原因の一つとして/mnt/c配下で作業していることが挙げられる。 /mntはWSL内からWindows側のファイルへアクセスするためにあるディレクトリで、/mnt/c配下はWindowsのCドライブのディレクトリを指す。つまり、Windows側のC:\は、WSLでは/mnt/cということ。
  • Windows側のファイルシステムを経由してしまうと、ビルドに時間がかかったり、一部の機能が制限されてしまうため、ファイル名の変更ができなかったり、ホットリロードが効かなかったりする
  • 解決策としては/mnt/c配下で作業するのを避け、WSLの~配下などで作業をすることで、期待通りの動きをしてくれるはず。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む