[Vue入門] 条件付きレンダリング

v-if

v-if ディレクティブは、ブロックを条件に応じてレンダリングしたい場合に使用されます。ブロックは、ディレクティブの式が真を返す場合のみレンダリングされます。

<h1 v-if="awesome">Vue is awesome!</h1>

v-else

v-if に対して “else block” を示すために、v-else ディレクティブを使用できます:

<button @click="awesome = !awesome">Toggle</button>

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

Toggle

Vue is awesome!

Try it in the Playground

v-else 要素は、v-if または v-else-if 要素の直後になければなりません。それ以外の場合は認識されません。

v-else-if

v-else-if は、名前が示唆するように、v-if の “else if block” として機能します。また、複数回連結することもできます:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

v-else と同様に、v-else-if 要素は v-if 要素または v-else-if 要素の直後になければなりません。

<template> での v-if

v-if はディレクティブなので、単一の要素に付加する必要があります。しかし、1 要素よりも多くの要素と切り替えたい場合はどうでしょうか?このケースでは、非表示ラッパー (wrapper) として提供される、<template> 要素で v-if を使用できます。最終的にレンダリングされる結果は、<template> 要素は含まれません。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-else と v-else-if は <template> でも使用可能です。

v-show

条件的に要素を表示するための別のオプションは v-show です。使用方法はほとんど同じです:

<h1 v-show="ok">Hello!</h1>

違いは v-show による要素は常レンダリングされて DOM に残るということです。v-show はシンプルに要素の display CSS プロパティを切り替えます。

v-show は <template> 要素をサポートせず、v-else とも連動しないということに注意してください。

v-if vs v-show

v-if は、イベントリスナと子コンポーネント内部の条件ブロックが適切に破棄され、そして切り替えられるまでの間再作成されるため、”リアル”な条件レンダリングです。

v-if はまた 遅延レンダリング (lazy) でもあります。 初期表示において状態が false の場合、何もしません。つまり条件付きブロックは、条件が最初に true になるまでレンダリングされません。

一方で、v-show はとてもシンプルです。要素は初期条件に関わらず常にレンダリングされ、シンプルな CSS ベースの切り替えによって表示されます。

一般的に、v-if はより高い切り替えコストを持っているのに対して、 v-show はより高い初期レンダリングコストを持っています。 そのため、とても頻繁に何かを切り替える必要があれば v-show を選び、条件が実行時に変更することがほとんどない場合は、v-if を選びます。

v-if と v-for

Note

暗黙的な優先順位により、 v-if と v-for を同じ要素で利用することは 推奨されません。 詳細については スタイルガイド を参照ください。

v-if と v-for が同じ要素に両方つかわれる場合、 v-if が先に評価されます。

[Vue入門] クラスとスタイルのバインディング

データバインディングは、HTML 要素に持たせる CSS クラスのリストやインラインのスタイルを自在に操作したいという、よくあるニーズに応えます。どちらも属性なので v-bind で扱うことができ、あとは式で最終的な文字列を算出すればよいだけです。しかし、文字列の結合に手を出すのは、手間がかかり、間違いが起きやすくなるものです。そこで、Vue では class や style に対して v-bind を用いるとき、特別な拡張が利用できるようになっています。文字列のほかに、オブジェクトまたは配列として評価される式も利用できます。

HTML クラスのバインディング

オブジェクトへのバインディング

:class (v-bind:class の省略記法) では、オブジェクトを渡して CSS クラスを動的に切り替えることができます:

<div :class="{ active: isActive }"></div>

上の構文は、コンポーネントのデータの isActive というプロパティが真値(true)であるかどうかによって active という CSS クラスを含めるかどうかを決定する、という意味になります。

オブジェクトのフィールドを増やせば、複数のクラスをトグルすることができます。さらに、:class ディレクティブは通常の class 属性と共存させることもできます。例えば、次のような状態があるとします:

const isActive = ref(true)
const hasError = ref(false)

そしてテンプレートが次のようになっているとします:

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>

このとき、レンダリング結果は次のようになります:

<div class="static active"></div>

isActive や hasError が変化すると、それに合わせてクラスのリストも更新されます。例えば、hasError が true になればクラスのリストは "static active text-danger" に変わります。

バインドするオブジェクトはインラインにしなくても構いません:

const classObject = reactive({
  active: true,
  'text-danger': false
})
<div :class="classObject"></div>

これも、同じレンダリング結果を得られます。オブジェクトを返す算出プロパティにクラスをバインドすることも可能です。次の例は、よく使われる強力なパターンです:

const isActive = ref(true)
const error = ref(null)

const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))
<div :class="classObject"></div>

配列へのバインディング

次のように :class を配列にバインドすると、クラスのリストを適用することができます:

const activeClass = ref('active')
const errorClass = ref('text-danger')
<div :class="[activeClass, errorClass]"></div>

レンダリング結果は次のようになります:

<div class="active text-danger"></div>

リストに含まれる特定のクラスを条件に基づいて切り替えたい場合には、三項演算子を使えば実現できます:

<div :class="[isActive ? activeClass : '', errorClass]"></div>

この場合、errorClass は常に適用され、activeClass は isActive が真のときだけ適用されます。

しかし、条件を付けたいクラスが複数あると、これでは少し冗長になります。そこで、配列構文の中でオブジェクト構文を使うこともできるようになっています:

<div :class="[{ active: isActive }, errorClass]"></div>

コンポーネントでの使用

このセクションは、コンポーネントについての知識があることを前提としています。スキップして、後から読み直すのでも大丈夫です。

ルート要素が 1 つだけのコンポーネントで class 属性を使用すると、そこで指定したクラスがコンポーネントのルート要素に追加され、すでに指定されている既存のクラスとマージされます。

例えば、my-component という名前のコンポーネントがあり、次のようなテンプレートになっているとします:

<!-- 子コンポーネントのテンプレート -->
<p class="foo bar">Hi!</p>

そして、コンポーネントを使う際にクラスをいくつか追加します:

<!-- コンポーネントを使用する時点 -->
<my-component class="baz boo"></my-component>

レンダリングされる HTML は次のようになります:

<p class="foo bar baz boo">Hi</p>

クラスバインディングでも同様です:

<my-component :class="{ active: isActive }"></my-component>

isActive が真値のとき、レンダリングされる HTML は次のようになります:

<p class="foo bar active">Hi</p>

コンポーネントに複数のルート要素を持たせているときは、どの要素にクラスを渡すか指定する必要があります。これは、以下のように $attrs コンポーネントプロパティを使って行います:

<!-- $attrs を使った my-component のテンプレート -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
<my-component class="baz"></my-component>

レンダリング結果は次のようになります:

<p class="baz">Hi!</p>
<span>This is a child component</span>

コンポーネントの属性の継承については、フォールスルー属性のセクションで詳しく説明しています。

インラインスタイルのバインディング

オブジェクトへのバインディング

:style では次のような JavaScript のオブジェクト値へのバインディングがサポートされ、HTML 要素の style プロパティ に対応します:

const activeColor = ref('red')
const fontSize = ref(30)
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

CSS プロパティのキーにはキャメルケース (camelCase) が推奨されますが、:style では CSS の実際の書き方に対応するケバブケース (kebab-cased) のキーもサポートされています。例:

<div :style="{ 'font-size': fontSize + 'px' }"></div>

テンプレートをすっきりさせるため、多くの場合、次のようにスタイルオブジェクトを直接バインドするとよいでしょう:

const styleObject = reactive({
  color: 'red',
  fontSize: '13px'
})
<div :style="styleObject"></div>

スタイルへのオブジェクトのバインディングも、オブジェクトを返す算出プロパティと組み合わせて使用することが多くあります。

配列へのバインディング

:style は、複数のスタイルオブジェクトからなる配列にバインドすることができます。各オブジェクトはマージされ、同じ要素に適用されます:

<div :style="[baseStyles, overridingStyles]"></div>

自動プレフィックス

:style で ベンダープレフィックスを必要とする CSS プロパティを指定すると、Vue が適切なプレフィックスを自動的に追加します。Vue は、実行時にブラウザーでどのスタイルプロパティがサポートされているかをチェックして、適切なものを追加します。特定のプロパティがブラウザーでサポートされていない場合、Vue はさまざまなプレフィックスのバリエーションをテストし、サポートされているものを見つけようと試みます。

複数の値

style プロパティには、プレフィックス付きを含む複数の値を、配列で指定することができます。例:

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

このように指定すると、配列に含まれる値のうち、ブラウザーでサポートされる最後の値のみがレンダリングに使われます。この例では、接頭辞なしのバージョンのフレックスボックスをサポートするブラウザーでは、display: flex がレンダリングに使われます。

[Vue入門]Computed Properties

基本的な例

テンプレート内に式を書けるのはとても便利ですが、非常に簡単な操作しかできません。テンプレート内に多くのロジックを詰め込むと、コードが肥大化し、メンテナンスが難しくなります。例えば、配列が入れ子(ネスト)になっているオブジェクトがあった場合:

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

そして、author がすでにいくつかの books を持っているかどうかによって、異なるメッセージを表示したいとします:

<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

この時点で、テンプレートが少しごちゃごちゃしてきました。しばらく眺めて、やっとこれが author.books に依存した計算をしていることに気づくでしょう。さらに重要なことは、同じ計算をテンプレートの中で複数回使う場合、おそらく繰り返して使いたくはないでしょう。

上記の理由から、リアクティブなデータを含む複雑なロジックには算出プロパティを使用すべきです。以下は上記と同じ例をリファクタリングしたものです:

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 算出プロパティの参照
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

プレイグラウンドで試す

ここでは、publishedBooksMessage という算出プロパティを宣言しています。computed() 関数は getter 関数が渡されることを想定しており、返り値は 算出された ref となります。通常の ref と同様に、publishedBooksMessage.value で算出結果を参照することができます。また、算出結果はテンプレート内では自動的にアンラップされるため、テンプレート内では .value なしで参照することができます。

算出プロパティは、自動的にリアクティブな依存関係を追跡します。Vue は publishedBooksMessage の算出が author.books に依存することを知っているので、author.books が変わると publishedBooksMessage に依存する全てのバインディングを更新します。

算出プロパティ vs メソッド

こういった式を持つメソッドを呼び出すことで、同じ結果が実現できることに気付いたかもしれません:

<p>{{ calculateBooksMessage() }}</p>
// コンポーネント内
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Yes' : 'No'
}

算出プロパティの代わりに、同じような関数をメソッドとして定義することもできます。最終的には、2 つのアプローチは完全に同じ結果になります。しかしながら、算出プロパティはリアクティブな依存関係にもとづきキャッシュされるという違いがあります。算出プロパティは、リアクティブな依存関係が更新されたときにだけ再評価されます。これはつまり、 author.books が変わらない限りは、publishedBooksMessage に何度アクセスしても、getter 関数を再び実行することなく、以前計算された結果を即時に返すということです。

Date.now() はリアクティブな依存ではないため、次の算出プロパティは二度と更新されないことを意味します:

const now = computed(() => Date.now())

対称的に、メソッド呼び出しは、再描画が起きると常に関数を実行します。

なぜキャッシングが必要なのでしょうか?巨大な配列をループしたり多くの計算を必要とする、コストの高い list という算出プロパティがあることを想像してみてください。list に依存する他の算出プロパティもあるかもしれません。その場合、キャッシングがなければ必要以上に list の getter を実行することになってしまいます。キャッシングしたくない場合は、代わりにメソッドを使いましょう。

書き込み可能な 算出関数

算出プロパティは、デフォルトでは getter 関数のみです。算出プロパティに新しい値を代入しようとすると、ランタイム警告が表示されます。まれに「書き込み可能な」算出プロパティが必要な場合があります。その場合は getter 関数と setter 関数の両方を提供することで、それを作成することができます:

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter 関数
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter 関数
  set(newValue) {
    // 注意: ここでは、破壊的な代入構文を使用しています。
    ;[firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

fullName = 'John Doe' を呼ぶと、setter 関数が呼び出され、firstName と lastName が適切に更新されます。

ベストプラクティス

getter 関数は副作用のないものでなければならない

算出プロパティにおける getter 関数は計算のみを行い、副作用がないようにすることが重要です。例えば、非同期リクエストや、DOM を変化させないようにしましょう!算出プロパティは他の値に基づいて計算する方法を宣言的に記述していると考えてください。その唯一の責任は、値を計算して返すことでなければなりません。このガイドの後半では、 ウォッチャー を使って、状態の変化に反応して副作用を実行する方法について説明します。

算出した値の変更を避ける

算出プロパティから返る値は、派生され状態です。一時的なスナップショットとして考えてください。ソースの状態が変わるたびに、新しいスナップショットが作成されます。スナップショットの値を変更することは意味がないため、計算された結果は読み取り専用として扱い、変更しないようにします。その代わり、新しい計算結果が必要な場合は、依存するソースの状態を更新します。

[Vue入門]リアクティビティの基礎

リアクティブな状態を宣言する

リアクティブなオブジェクトや配列を作るには、reactive() 関数を使用します。

import { reactive } from 'vue'

const state = reactive({ count: 0 })

リアクティブなオブジェクトは JavaScript プロキシ で、通常のオブジェクトと同じように振る舞います。違いは、Vue がリアクティブなオブジェクトのプロパティアクセスと変更を追跡できることです。詳細については、Reactivity in Depth で Vue のリアクティブシステムの仕組みを説明していますが、このメインガイドを読み終えた後に読むことをお勧めします。

コンポーネントのテンプレートでリアクティブな状態を使うには、下記に示すように、コンポーネントの setup() 関数で宣言し、それを返します:

import { reactive } from 'vue'

export default {
  // `setup` 関数は、Composition API 専用の特別なフックです。
  setup() {
    const state = reactive({ count: 0 })

    // 状態をテンプレートに公開します
    return {
      state
    }
  }
}
<div>{{ state.count }}</div>

同様に、リアクティブな状態を変化させる関数を同じスコープで宣言し、状態と並行してメソッドとして公開することができます:

import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({ count: 0 })

    function increment() {
      state.count++
    }

    // 関数も公開することを忘れないでください。
    return {
      state,
      increment
    }
  }
}

通常、公開されたメソッドはイベントリスナーとして使用されます。

<button @click="increment">
  {{ state.count }}
</button>

<script setup>

setup() 関数を使って手動で状態やメソッドを公開すると、冗長になることがあります。幸いなことに、これはビルドステップを使用しない場合にのみ必要です。単一ファイルコンポーネント (SFC) を使用する場合は、 <script setup> を使用することで大幅に簡略化することができます。

<script setup>
import { reactive } from 'vue'

const state = reactive({ count: 0 })

function increment() {
  state.count++
}
</script>

<template>
  <button @click="increment">
    {{ state.count }}
  </button>
</template>

プレイグラウンドで試す

トップレベルのインポートと <script setup> で宣言された変数は、同じコンポーネントのテンプレートで自動的に使用できるようになります。

当ページ残りの部分では、Composition API のコード例として主に SFC + <script setup> という構文を使用します。

DOM 更新のタイミング

リアクティブな状態を変化させると、DOM は自動的に更新されます。しかし、DOM の更新は同期的に適用されないことに注意する必要があります。その代わりに Vue は、更新サイクルの「next tick」まで更新をバッファリングし、どれだけ状態を変化させても、各コンポーネントは一度だけ更新する必要があることを保証しています。

状態変化後の DOM 更新が完了するのを待つため、nextTick() というグローバル API を使用することができます:

import { nextTick } from 'vue'

function increment() {
  state.count++
  nextTick(() => {
    // DOM 更新にアクセスします
  })
}

ディープなリアクティビティ

Vue では、デフォルトで状態がリアクティブになっています。つまり、ネストしたオブジェクトや配列を変化させた場合でも、変更が検出されることが期待できます:

import { reactive } from 'vue'

const obj = reactive({
  nested: { count: 0 },
  arr: ['foo', 'bar']
})

function mutateDeeply() {
  // これらは期待通りに動作します。
  obj.nested.count++
  obj.arr.push('baz')
}

また、ルートレベルでのみリアクティビティを追跡する shallow reactive object を明示的に作成することも可能ですが、これらは一般的に高度な使用例においてのみ必要とされるものとなります。

リアクティブプロキシ vs. 独自

注意すべきは、reactive() の戻り値が、元のオブジェクトの プロキシ であり、元のオブジェクトと等しくないということです:

const raw = {}
const proxy = reactive(raw)

// プロキシはオリジナルと同じではありません。
console.log(proxy === raw) // false

プロキシだけがリアクティブとなります。元のオブジェクトを変更しても更新は行われません。したがって、Vue のリアクティブシステムを使用する際のベストプラクティスは、プロキシされた状態のバージョンだけを使用することになります

プロキシへの一貫したアクセスを保証するために、同じオブジェクトに対して reactive() を呼ぶと常に同じプロキシを返し、既存のプロキシに対して reactive() を呼ぶとその同じプロキシも返されます。

// calling reactive() on the same object returns the same proxy
console.log(reactive(raw) === proxy) // true

// calling reactive() on a proxy returns itself
console.log(reactive(proxy) === proxy) // true

このルールは、ネストされたオブジェクトにも適用されます。深いリアクティビティを持つため、リアクティブなオブジェクトの中にあるネストされたオブジェクトもプロキシとなります。

const proxy = reactive({})

const raw = {}
proxy.nested = raw

console.log(proxy.nested === raw) // false

reactive() の制限

reactive() API には 2 つの制限があります:

  1. オブジェクト型 (オブジェクト、配列、および Map や Set などの コレクション型) に対してのみ機能します。文字列、数値、ブールなどの プリミティブ型 を保持することはできません。
  2. Vue のリアクティビティ追跡はプロパティアクセス上で動作するため、リアクティブなオブジェクトへの参照を常に同じに保つ必要があります。つまり、最初の参照へのリアクティブな接続が失われるため、リアクティブなオブジェクトを簡単に「置き換える」ことはできません:let state = reactive({ count: 0 }) // 上記の参照({ count: 0 })は、もはや追跡されていません(リアクティブな接続が失われました!) state = reactive({ count: 1 }) また、リアクティブなオブジェクトのプロパティをローカル変数に代入したり、分割代入したり、そのプロパティを関数に渡したりすると、下記に示すようにリアクティブなつながりが失われることとなります:const state = reactive({ count: 0 }) // n は切り離されたローカル変数 // を state.count から取得します。 let n = state.count // 元の状態に戻りません。 n++ // count も state.count と切り離されます。 let { count } = state // 元の状態に戻りません。 count++ // この関数が受け取る平文番号と // state.count の変更を追跡することができません。 callSomeFunction(state.count)

ref() と共に使うリアクティブな変数

Vue は、reactive() の制限に対処するため、ref() という関数も提供しており、任意の値の型を保持できるリアクティブな “refs “ を作成することができます:

import { ref } from 'vue'

const count = ref(0)

ref() は引数を受け取り、それを .value プロパティを持つ ref オブジェクトにラップして返します:

const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

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

リアクティブなオブジェクトのプロパティと同様に、ref の .value プロパティはリアクティブとなります。また、オブジェクト型を保持する場合、ref は .value を reactive() で自動的に変換します。

オブジェクトの値を含む ref は、オブジェクト全体をリアクティブに置き換えることができます:

const objectRef = ref({ count: 0 })

// これはリアクティブに動きます。
objectRef.value = { count: 1 }

また、Ref を関数に渡したり、プレーンオブジェクトから分解したりしても、リアクティビティが失われることはありません。

const obj = {
  foo: ref(1),
  bar: ref(2)
}

// ref を受け取るこの関数は、
// .value を介して値にアクセスする必要がありますが、それは
// リアクティビティを保持します。
callSomeFunction(obj.foo)

// リアクティビティを保持しています。
const { foo, bar } = obj

つまり、ref() を使うと、任意の値への「参照」を作り、リアクティビティを失わずに受け渡しすることができます。この能力は、ロジックを Composable Functions に抽出する際に頻繁に使用されるため、非常に重要となります。

Ref Unwrapping in Templates

ref がテンプレートのトップレベルのプロパティとしてアクセスされた場合、それらは自動的に「アンラップ」されるので、.value を使用する必要はありません。以下は、先ほどのカウンターの例で、代わりに ref() を使用したものとなります:

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }} <!-- .value は必要ありません -->
  </button>
</template>

プレイグラウンドで試す

アンラップは、ref がテンプレートに描画されるコンテキスト上のトップレベルのプロパティである場合にのみ適用されることに注意してください。例として foo はトップレベルのプロパティですが、object.foo はトップレベルではありません。

そこで、下記に示したようなオブジェクトが与えられた:

const object = { foo: ref(1) }

下記に示した式は、期待通りに動作 しません :

{{ object.foo + 1 }}

レンダリング結果は [object Object] となります。これは object.foo が ref オブジェクトであるためです。これを解決するには、下記に示すように foo をトップレベルのプロパティにします:

const { foo } = object
{{ foo + 1 }}

これで、レンダリング結果は「2」になります。

注意点としては、ref がテキスト補間の最終評価値(つまり {{ }} タグ)である場合もアンラップされるので、以下のように 1 がレンダリングされます。

{{ object.foo }}

これはテキスト補間の便利な機能に過ぎず、 {{ object.foo.value }} と等価になります。

リアクティブなオブジェクトにおける Ref のアンラッピング

リアクティブなオブジェクトのプロパティとして ref にアクセスしたり変化させたりすると、自動的にアンラップされるので、通常のプロパティと同じように振る舞うことができます:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

既存の ref にリンクされたプロパティに新しい ref が割り当てられた場合、下記に示すように、それは古い ref を置き換えることとなります:

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
// 元の ref は state.count から切り離されました。
console.log(count.value) // 1

Ref のアンラッピングは、より深いリアクティブなオブジェクトの内部にネストされている場合にのみ発生します。浅いリアクティブなオブジェクト のプロパティとしてアクセスされた場合は適用されません。

配列とコレクションにおける Ref のアンラッピング

リアクティブなオブジェクトと異なり、ref がリアクティブな配列の要素や、Map のようなネイティブコレクション型としてアクセスされた場合には、アンラップは行われません。

const books = reactive([ref('Vue 3 Guide')])
// ここでは .value が必要となります
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// ここでは .value が必要となります
console.log(map.get('count').value)

Reactivity Transform 

Ref で .value を使わなければならないのは、JavaScript の言語的な制約による欠点です。しかし、コンパイル時の変換 (ここでいうコンパイル時とは SFC を JavaScript コードへ変換する時) を利用すれば、適切な場所に自動的に .value を追加して人間工学を改善することができます。Vue はコンパイル時の変換を提供しており、先ほどの「カウンター」の例をこのように記述することができます。

<script setup>
let count = $ref(0)

function increment() {
  // ここでは .value が不要です
  count++
}
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

Reactivity Transform の詳細については、専用のセクションで説明されています。ただし、現在はまだ実験的なものであり、最終的に完成するまでに変更される可能性があることに注意してください。

Vue Options APIとCompostion APIの違い

これからVueJSを学ぶ方へ。

VueはReactやAngularと同じようにSPA(Single Page Application)を作るためのJavaScriptフレームワークです。

Reactが統計上では一番人気で求人も多いという事ですが、Vueは初心者にもわかりやすく、数年でReact以上の人気が出ると思います。

現段階ではVueはバージョン3まで出ており、今から始めるなら最新のVue3から学び始めて問題ありません。

Options APIとCompostion API

Options API

Vueのコードを書くにあたり2つ(正確には3つ)方法があります。

一つ目が初心者向けのOptions APIです。

まずはOptionsAPIの例を見てみましょう。

<script>
export default {
  // Properties returned from data() becomes reactive state
  // and will be exposed on `this`.
  data() {
    return {
      count: 0
    }
  },

  // Methods are functions that mutate state and trigger updates.
  // They can be bound as event listeners in templates.
  methods: {
    increment() {
      this.count++
    }
  },

  // Lifecycle hooks are called at different stages
  // of a component's lifecycle.
  // This function will be called when the component is mounted.
  mounted() {
    console.log(`The initial count is ${this.count}.`)
  }
}
</script>

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

Options APIの特徴はdata()でデータをまとめmethod()で使用するファンクションをまとめ、このようにブロックごとでコードを分けていくことです。

短所として、thisのキーワードを使わないといけないこと、ブロックごとでコードをまとめるのでコードの文にまとまりがなくなることです。

一応、Options APIという存在があるという事だけ覚えておいて、まったく学ぶ必要はありません。

Composition API

Vueの強みといえるCompostion APIと<script setup>を紹介します。

下記がcompostion APIの例になります。

特徴としては、

  • <script setup>が導入され少ないコードでかけるようになった。
  • setupのキーワードをscriptタグに入れた場合はvueからメソッドをインポートする必要があります。
  • 機能ごとにコードをまとめるので後から読みやすい。
  • インポートしたコンポーネントや変数をそのままHTMLのテンプレートで使用できる。
<script setup>
import { ref, onMounted } from 'vue'

// reactive state
const count = ref(0)

// functions that mutate state and trigger updates
function increment() {
  count.value++
}

// lifecycle hooks
onMounted(() => {
  console.log(`The initial count is ${count.value}.`)
})
</script>

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

ではrefやonMountのLifecycle Hook(ライフサイクルフック)は今後、基本のコンポーネントやプロップの使い方を紹介した後に詳しく説明していきたいと思います。

今日、覚えてほしいことです。

  • VueはCompostion APIの書き方で書くようにする。
  • VueはJavaScriptとhtml、cssが一つのファイルにまとまった.vueファイルで構成される。

Viteを使ってVueを始めよう

Viteとは

Vite(発音:ヴィート)はVue Cliの次世代のビルドツールで軽量でデベロップメントのスピードを速くするために作られました。

今まではnpmやvue cliでVueアプリを作ってきましたが、Viteを使ってVueだけでなくReactやSvelteも構築できてしまいます。


Viteをつかってみよう

現段階ではNode.jsの12.2.0以上のバージョンが必要とされています。ではnpm、もしくはyarnが入っていることを確認してViteを使ってみましょう。

npmの場合

npm create vite@latest

yarnの場合

yarn create vite

インストールが終わったら下のコマンドでパッケージをインストールしてデベロップメント用のウェブサーバーを起動しましょう。

  cd vite-project
  npm install
  npm run dev

index.html と プロジェクトルート

通常、Vue Cliやnpmで作成したVueアプリケーションのindex.htmlはpublicフォルダに格納されますが、Viteで作った場合はindex.htmlがプロジェクトの直下に作成されます。このファイルがアプリケーションのエントリーポイントとなっているからです。

アプリケーションのビルド

Viteでアプリケーションが完成したら下記のコマンドでコードをコンパイルしましょう。

npm run build

 

Vue3 のドキュメンテーションを日本語でみる方法

Vueの公式ドキュメンテーションを見てみるとまだ日本語の翻訳は準備中の様です。

しかし、翻訳したリポジトリをクローンして読むことができるので是非試してみましょう。

翻訳のGitHubページ

https://github.com/vuejs-translations/docs-ja

上記のリンクからGitを使ってリポジトリをローカル環境にクローンしましょう。

Gitが無い人はこちらを先に見てくださいね。

pnpmのインストール

リポをクローンする前に先にpnpmをインストールしておきましょう。

npm install -g pnpm

リポのクローン

ではリポジトリをクローンしていきます。

git clone https://github.com/vuejs-translations/docs-ja.git
cd docs-ja

pnpm i
pnpm run dev

で、URLがコマンドラインに表示されたらそこからドキュメンテーションを見てみましょう。

私はいつも英語ドキュメンテーションを読みますが翻訳するのが大変なのでこれは助かります!

お疲れっす。

[Vue入門] テンプレートシンタックス

Vue では、HTML ベースのテンプレート構文を使用します。テンプレート構文では、基盤とするコンポーネントのインスタンスのデータと、レンダリングされる DOM を宣言的にバインドすることが可能です。すべての Vue テンプレートは、仕様に準拠しているブラウザーや HTML パーサーでパースできる、構文的に正規の HTML です。

内部では、Vue はテンプレートをコンパイルし、高度に最適化された JavaScript のコードにします。リアクティビティ機構と組み合わせ、Vue はアプリの状態が変化したとき、再レンダリングを必要とする最小限のコンポーネントをインテリジェントに見つけ出すことができます。そして、最小限の DOM 操作を適用します。

仮想 DOM の各種概念をよく知っていて、生の JavaScript が持つパワーを活かしたいという場合には、テンプレートの代わりに render 関数を直接記述することもできます。さらに、オプションで JSX もサポートされています。ただし、これらの書き方をする場合には、コンパイル時の最適化がテンプレートと同等のレベルでは利用できないことに注意してください。

テキスト展開

データバインディングで最も基本の形式は、「マスタッシュ構文」(二重中括弧) によるテキスト展開です:

<span>Message: {{ msg }}</span>

マスタッシュのタグの中身は、対応するコンポーネントのインスタンスが持つ msg というプロパティの値に置き換えられます。msg プロパティが変更されるたびに、マスタッシュの中身も更新されます。

HTMLタグを含めたデータ

マスタッシュの中では、データが HTML ではなくプレーンテキストとして解釈されます。本来の HTML を出力したい場合は、次のように v-html ディレクティブを用いる必要があります:

<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

Using text interpolation: <span style=”color: red”>This should be red.</span>

Using v-html directive: This should be red.

ここで、新たな要素が登場しました。この例にある v-html という属性は、「ディレクティブ」と呼ばれるものです。ディレクティブは v- という接頭辞を持ち、Vue によって提供される特別な属性であることを示します。そしてご想像の通り、ディレクティブはレンダリングされる DOM に、特別なリアクティブな振る舞いを割り当てます。この例では、簡単に言うと、「現在アクティブなインスタンスが持つ rawHtml というプロパティをこの要素の inner HTML に適用して最新に保つ」ということが書かれています。

span の中身は rawHtml プロパティが持つ値に置き換えられ、プレーンな HTML として解釈されます。データバインディングは無視されます。v-html は、テンプレートの断片を組み立てるのには利用できないことに注意してください。これは、Vue が文字列ベースのテンプレートエンジンではないためです。それに代わり、UI の再利用や組み立ての基本単位として推奨されているのが「コンポーネント」です。

セキュリティーに関する警告

ウェブサイト上で任意の HTML を動的にレンダリングすることは、クロスサイトスクリプティング (XSS) 脆弱性を招きやすく、非常に危険です。v-html は信頼できるコンテンツにのみ使用し、ユーザーから渡されるコンテンツには決して使用しないでください。

属性バインディング

HTML 属性の中ではマスタッシュ構文が使えません。代わりに、以下の v-bind ディレクティブを使用します:

<div v-bind:id="dynamicId"></div>

この v-bind ディレクティブは、要素の id という属性を、コンポーネントが持つ dynamicId というプロパティと同期させるよう Vue に指示しています。バインドされた値が null または undefined の場合、その属性はレンダリングされる要素から除外されます。

省略記法

v-bind は使用頻度が非常に高いため、以下の専用の省略記法があります:

<div :id="dynamicId"></div>

: で始まる属性は、普通の HTML の記法とは少し異なるように見えますが、実際には属性名として有効な文字です。Vue をサポートするすべてのブラウザーは、これを正しく解析することができます。なお、これは最終的にレンダリングされるマークアップには現れません。この省略記法を使うかどうかは任意ですが、その使い方を後ほど詳しく知れば、良さがわかるはずです。

このガイドの残りの部分では、Vue を用いる開発者にとって最も一般的な書き方である省略記法をコード例のなかで使用します。

ブーリアン属性

ブーリアン属性は、要素に含まれるかどうかによって「真」または「偽」の値を表す属性です。例えば、disabled は最も一般的に用いられるブーリアン属性の 1 つです。

以下のケースでは、v-bind は少し特別な動作をします:

<button :disabled="isButtonDisabled">Button</button>

この disabled という属性は、isButtonDisabled が 真値 (truthy value) である場合に要素に含まれます。また、<button disabled=""> との一貫性を保つため、値が空の文字列である場合にも含まれます。それ以外の偽値 (falsy values) の場合には、属性が要素から取り除かれます。

複数の属性を動的にバインドさせる

次のような複数の属性を持つ JavaScript オブジェクトがあるとします:

data() {
  return {
    objectOfAttrs: {
      id: 'container',
      class: 'wrapper'
    }
  }
}

以下のように v-bind を引数なしで用いると、これらの複数の属性を 1 つの要素にバインドすることができます:

<div v-bind="objectOfAttrs"></div>

JavaScript の式を用いる

ここまで、テンプレート内のプロパティのキーに単純なバインドを行う方法だけを見てきました。しかし、実は Vue ではあらゆるデータバインディングにおいて、以下のように JavaScript 式のフルパワーを活用することができます:

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

これらの式は、現在のコンポーネントインスタンスのデータスコープ内で、JavaScript の式として評価されます。

Vue のテンプレートでは、以下の場所で JavaScript の式を使用することができます:

  • テキスト展開の内部 (マスタッシュ構文内)
  • 任意の Vue ディレクティブ (v- で始まる特殊な属性) の属性値の中身

式に限られる

それぞれのバインディングには、単一の式しか含めることができません。そのため、以下はうまく動作しません:

<!-- これは文であり、式ではありません: -->
{{ var a = 1 }}

<!-- フロー制御も動作しません。代わりに三項演算子を使用してください。 -->
{{ if (ok) { return message } }}

関数の呼び出し

コンポーネントから公開されているメソッドであれば、以下のようにバインディングの式の内部で呼び出すことができます:

<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

TIP

バインディングの式の内部で呼び出される関数は、コンポーネントが更新されるたびに呼び出されます。そのため、データの変更や非同期処理をトリガーするような副作用を持たせてはいけません

グローバルへのアクセスの制限

テンプレートで用いる式はサンドボックス内で実行され、限定的なグローバルのリストにのみアクセスできます。このリストには、Math や Date などのよく使われる組み込みグローバルが含まれています。

ユーザーが window に付与したプロパティなど、このリストに明示的に含まれていないグローバルには、テンプレート内の式からアクセスすることができません。ただし、app.config.globalProperties に追加することにより、Vue のあらゆる式で利用できるグローバルを明示的に定義することができます。

ディレクティブ

ディレクティブは、v- という接頭辞を持つ特別な属性です。Vue では、上で紹介した v-html や v-bind をはじめ、数々の組み込みディレクティブが用意されています。

ディレクティブの属性値は、JavaScript の単一の式であることが期待されます (ただし v-forv-onv-slot は例外であり、後ほどそれぞれのセクションで説明します)。ディレクティブの役割は、式が示す値が変化したとき、リアクティブに更新を DOM に適用することです。例えば、v-if を取り上げてみます:

<p v-if="seen">Now you see me</p>

この例では、v-if というディレクティブを用いて、式 seen が示す値の真偽に基づいて要素 <p> を削除したり挿入したりします。

引数

一部のディレクティブは引数を取ることができます。引数は、ディレクティブ名の後にコロンで示します。以下は、v-bind ディレクティブを使って HTML 属性の 1 つをリアクティブに更新する例です:

<a v-bind:href="url"> ... </a>

<!-- 省略記法 -->
<a :href="url"> ... </a>

この例では href が引数です。これにより、要素の href という属性を url という式の値にバインドするという指示が v-bind ディレクティブに伝えられます。省略記法では、引数の前に置かれる v-bind: の部分がすべて : という 1 文字に凝縮されます。

別の例として、DOM イベントをリッスンする v-on ディレクティブを紹介します:

<a v-on:click="doSomething"> ... </a>

<!-- 省略記法 -->
<a @click="doSomething"> ... </a>

この例では、リッスンするイベント名の click が引数です。ごく一部のディレクティブには省略記法の記号として @ を持つものがあり、v-on もその 1 つです。イベントのハンドリングについても、後ほど詳しく説明します。

動的引数

ディレクティブの引数を指す部分では、以下のように角括弧で囲んだ JavaScript の式を用いることもできます:

<!--
引数で使用できる式には、いくつか制約があります。詳細は以下の
「動的引数の値に関する制約」および「動的引数の構文上の制約」セクションで説明します。
-->
<a v-bind:[attributeName]="url"> ... </a>

<!-- 省略記法 -->
<a :[attributeName]="url"> ... </a>

この例では、attributeName が JavaScript の式として動的に評価され、そこで評価された値が最終的な引数を指す値として使用されます。例えば、コンポーネントのインスタンスが attributeName というデータプロパティを持ち、その値が "href" のとき、このバインディングは v-bind:href と同等になります。

同じように、動的引数を用いてハンドラーを動的なイベント名にバインドすることもできます:

<a v-on:[eventName]="doSomething"> ... </a>

<!-- 省略記法 -->
<a @[eventName]="doSomething">

この例では、eventName の値が "focus" のとき、v-on:[eventName] が v-on:focus と同等になります。

動的引数の値に関する制約

動的引数は、評価結果が null または文字列のいずれかになることが期待されます。null は特別な値で、バインディングを削除することを明示的に表します。それ以外の非文字列の値を指定すると、警告が発生します。

動的引数の構文上の制約

動的引数の式には、構文上の制約がいくつかあります。これは、スペースや引用符など特定の文字が HTML の属性名の中では無効となるためです。例えば、次のようなものは無効となります:

<!-- この場合、コンパイラーで警告が発生します。 -->
<a :['foo' + bar]="value"> ... </a>

複雑な動的引数を渡す必要がある場合は、後ほど取り上げる算出プロパティを使用するとよいでしょう。

また、HTML ファイルに直接記述する DOM 内テンプレートを使用する場合、ブラウザーでは属性名が小文字であることが求められるため、以下のように大文字のキー名を使用することは避ける必要があります:

<a :[someAttr]="value"> ... </a>

上のコードは、DOM 内テンプレートでは :[someattr] に変換されます。もしコンポーネントに someattr ではなく someAttr というプロパティしかなければ、このコードは動作しません。

修飾子

修飾子は、ドット (.) で示される特別な接頭辞で、ディレクティブを何らかの特別な方法でバインドすることを表します。例えば、以下に示す .prevent という修飾子は、イベントがトリガーされたときに event.preventDefault() を呼び出すことを v-on ディレクティブに伝えます:

<form @submit.prevent="onSubmit">...</form>

この後、v-on 向けや v-model 向けの修飾子の例を、その機能のページで見ることになるでしょう。

最後に、ディレクティブの構文の全容をこちらの図にまとめました:

Vue JSを始めよう!

準備しておくこと

・NodeJS Version 10~

今回使用するもの

・VueJS バージョン3

公式ドキュメンテーション

https://vuejs.org/guide/introduction.html

イントロ

VueJSはフロントエンドのJavaScriptフレームワークでユーザーインターフェースを作ることができます。

通常のHTML、CSS、JavaScriptに合わせてビルドでき、コンポーネントという概念で開発をより効率よくできます。

他の主要概念として、Declarative Rendering(宣言的レンダリング)やReactivity(反応性)などがありますが、まずは実際にVueJSを触りながら理解していきましょう。

ここでVueJSを開発するうえで2種類のAPIのスタイルがあることを簡単に説明します。この2つはOptionsAPIとCompositionAPIになりますが、CompositionAPIはOptionsAPIの上にできたものなので、まずはOptionsAPIから学ぶことをお勧めします。

ビルドツール

では、早速vueのアプリケーションを作成していきましょう。

Npmでもできますが、今日はvue cliというツールを使っていきます。他にvite(ヴィート)というビルドツールも人気が出てきましたね。色々選択肢が多くてほんと追いつけないっすね。

npmでインストールする方法

このコマンドです。

npm init vue@latest

Vue cliのインストール

npm install -g @vue/cli

# OR

yarn global add @vue/cli


Vue cliのバージョンを確認する

vue --version

Vue cliのバージョンを更新する

npm update -g @vue/cli
# OR

yarn global upgrade --latest @vue/cli

Vue CliでVueプロジェクトを作成する

vue create hello-world

#Helloe-worldの所は自分のプロジェクト名

ここでマニュアルで何をインストールしたいか選択できます。

また、これをプリセットとして保存して再度使う事もできるので便利ですね。

ファイル構成を見る

.gitはバージョンコントロールのGITファイルがあります。

Node_moduleにインストールしたパッケージが格納されます。

例えばBootstrapとか、ChartJSとかですね。

publicにはfav-iconとindex.htmlがあり、vueJSがこの中のid=”app”の部分にアプリをマウントすることになります。

Scrはソースフォルダです。

ここにコンポーネント、ルーティング、ビューのファイルが格納されます。

assetsはグローバルのCSSとか画像を保管するときに使います。

で、App.jsがVueの親のアプリのようなものです。

Main.jsがこの親アプリをどうするか指示するためのファイルになります。

では詳しいファイルの内容は動画で見てくださいね。

お疲れ様です。