Published on

Vue3とNuxt3基礎

Authors
  • avatar
    Name
    Kikusan
    Twitter

Vue

script と script setup の違い

Options API と Composition API の違い。 今後使う場合は Composition API が良い。 参考: https://ja.vuejs.org/guide/extras/composition-api-faq.html#why-composition-api

リアクティビティ

リアクティブな値とは、JavaScript のProxygetter,setterによって vue が値が変更されたことを傍受できる値のこと。vue が内部でreactiverefによって行ってくれる。
変更が検知されたタイミングで、DOM が更新される。(逐一ではなく、nextTickのタイミングで。)

ref

CompositionAPI ではリアクティブな変数をref()で宣言する。
template 内では.valueは必要なく、script では.valueで値にアクセスする。
ref はネストしたオブジェクトでもリアクティブにできる。
shallowRefによってパフォーマンスを考慮してオブジェクト単体のみをリアクティブにすることもできる。

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

<script setup>
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
</script>

reactive

reactiveはオブジェクト型をリアクティブにする。
プリミティブ型には機能しない。
直接オブジェクトを代入したり、分割代入したりするとリアクティビティ追跡が切れる。

リアクティブな状態を宣言するための主要な API として ref() を使用することを推奨します。

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

<script setup>
const state = reactive({ count: 0 })
</script>

computed

リアクティブなデータを含むロジックによる戻り値を得たい場合、computedを使用する。
戻り値を算出プロパティといい、refが返却される。
算出プロパティはキャッシュされ、内部のリアクティブな値が変更されるまで計算されない。

<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>

computedはメソッドが引数の場合は getter だけだが、object を渡すことで 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(' ')
  },
})

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

watch

リアクティブな値が変更された時に副作用を実行できる。

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

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')

// watch 関数は ref を直接扱えます
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.indexOf('?') > -1) {
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    }
  }
})
</script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</template>

いろいろな ref の渡し方がある。

const x = ref(0)
const y = ref(0)
const obj = reactive({ count: 0 })

// 単一の ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})
// オブジェクトを渡した場合はすべてのネストしたプロパティの変更を検知する

// getter (オブジェクトの内部プロパティを監視する場合に使用)
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

// 複数のソースの配列
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

watchEffectを使用すると、内部のリアクティブな値を自動的に追跡する。
さらにネストしたデータ構造については使用されるプロパティのみを追跡してくれる。

// todoIdを引数に渡す必要がない
watchEffect(async () => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
  data.value = await response.json()
})

デフォルトでは watch は DOM の更新前に動作する。
DOM の更新後に DOM にアクセスしたい場合: LINK

ディレクティブ

v-で始まるテンプレート内の属性値

v-bind

v-bindディレクティブは、:で記載ができる。
コンポーネントが持つプロパティ(変数)と同期させるよう指示ができる。
オブジェクトや関数によってタグの属性を設定することも可能。

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

<!-- ブーリアン属性 -->
<button :disabled="isButtonDisabled">Button</button>

<!-- v-bindを引数なしで渡すとキー名が属性名となり渡される -->
<div v-bind="objectOfAttrs"></div>

<!-- 式も使用可能 -->
<div :id="`list-${id}`"></div>

v-on

v-onディレクティブは、@で記載ができる。
直接実行する式や、イベントによって実行するメソッドを定義できる。
修飾子によって様々に効果を付帯できる。

<button @click="count++">Add 1</button>

<!-- サブミットイベントはもはやページをリロードしません -->
<form @submit.prevent="onSubmit"></form>

コンポーネントでは$emitdefineEmitsを使用して、親でイベントを購読できる。

defineEmitsではオブジェクトで定義することもでき、その際はバリデーションを走らせ emit を中断できる。

v-model

v-modelは bind と event を同時に定義できる。
checkbox や radiobutton などは配列と bind が可能。
文字でないものも bind できる。
修飾子によって様々な効果を付帯できる。
もしイベントをカスタムしたい場合は、v-onv-bindを使用する。

<!-- searchTextはリアクティブな変数 -->
<input :value="searchText" @input="searchText = $event.target.value" />
<!-- = -->
<input v-model="searchText" />

<!-- messageはinputの入力によって値が変わる -->
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />

コンポーネントでは props と emit に展開される。

引数を使用すると props と emit の名前を指定できる。

テンプレート参照の ref

template 内で ref を使用することで、DOM が更新された後の要素を取得できる。

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

// 要素の参照を保持する ref を宣言します。
// 名前は、テンプレートの ref の値に一致させる必要があります。
const input = ref(null)

onMounted(() => {
  input.value.focus()
})
</script>

<template>
  <input ref="input" />
</template>

コンポーネント

props

フォールスルー属性

子コンポーネントが受け入れていない属性値は、ルート要素の属性値として追加される。

<!-- <MyButton> の テンプレート -->
<button class="btn">click me</button>
<!-- buttonでonClickが動作する -->
<MyButton @click="onClick" />

ルートノードがない場合は警告がでて継承されない。
継承はinheritAttrsによって無効化できる。

<script setup>
defineOptions({
  inheritAttrs: false,
})
</script>

フォールスルー属性は$attrsでアクセスできる。

<span>Fallthrough attributes: {{ $attrs }}</span>

slot

親からタグの中身を渡してもらうことができる。

動的コンポーネント

コンポーネントを動的に切り替えることが可能。

<!-- currentTab 変更時にコンポーネントが変わります -->
<component :is="tabs[currentTab]"></component>

:isには以下を含めることができる。

  • 登録されたコンポーネントの文字列
  • 実際にインポートされたコンポーネントオブジェクト

provide/inject

親から発行したものを、その子孫で参照できる。

<!-- プロバイダーコンポーネント内部 -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation,
})
</script>
<!-- インジェクターコンポーネント内部 -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

Composable

同じロジックの vue の機能をコンポーザブル関数として外部に抽出できる。

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 慣習として、コンポーザブル関数の名前は "use" で始めます
export function useMouse() {
  // コンポーザブルによってカプセル化および管理される状態
  const x = ref(0)
  const y = ref(0)

  // コンポーザブルは管理している状態を時間の経過とともに更新できます。
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // コンポーザブルは所有コンポーネントのライフライクルにフックして
  // 副作用のセットアップや破棄することもできます。
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 管理された状態を戻り値として公開
  return { x, y }
}
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

プラグイン

プラグインについて: LINK

nuxt

under construction...