- Published on
Vue3とNuxt3基礎
- Authors
- Name
- Kikusan
- Vue
- script と script setup の違い
- リアクティビティ
- ref
- reactive
- computed
- watch
- ディレクティブ
- v-bind
- v-on
- v-model
- テンプレート参照の ref
- コンポーネント
- props
- フォールスルー属性
- slot
- 動的コンポーネント
- provide/inject
- Composable
- プラグイン
- nuxt
Vue
script と script setup の違い
Options API と Composition API の違い。 今後使う場合は Composition API が良い。 参考: https://ja.vuejs.org/guide/extras/composition-api-faq.html#why-composition-api
リアクティビティ
リアクティブな値とは、JavaScript のProxy
やgetter,setter
によって vue が値が変更されたことを傍受できる値のこと。vue が内部でreactive
やref
によって行ってくれる。
変更が検知されたタイミングで、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
ディレクティブは、@
で記載ができる。
直接実行する式や、イベントによって実行するメソッドを定義できる。
修飾子によって様々に効果を付帯できる。
- https://ja.vuejs.org/guide/essentials/event-handling.html
- イベント/キー/マウスボタン修飾子: https://ja.vuejs.org/guide/essentials/event-handling.html#event-modifiers
<button @click="count++">Add 1</button>
<!-- サブミットイベントはもはやページをリロードしません -->
<form @submit.prevent="onSubmit"></form>
コンポーネントでは$emit
、defineEmits
を使用して、親でイベントを購読できる。
defineEmits
ではオブジェクトで定義することもでき、その際はバリデーションを走らせ emit を中断できる。
v-model
v-model
は bind と event を同時に定義できる。
checkbox や radiobutton などは配列と bind が可能。
文字でないものも bind できる。
修飾子によって様々な効果を付帯できる。
もしイベントをカスタムしたい場合は、v-on
とv-bind
を使用する。
- https://ja.vuejs.org/guide/essentials/forms.html#form-input-bindings
- https://ja.vuejs.org/guide/components/v-model.html
- Vue.js の v-model を正しく使う
<!-- 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
- https://ja.vuejs.org/guide/components/props.html
- props はキャメルケースで定義して、スネークケースで参照する
- 子コンポーネントから props の値を変更することはしない
- 型定義による 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
親からタグの中身を渡してもらうことができる。
- https://ja.vuejs.org/guide/components/slots.html
- デフォルトの値を定義できる
- 名前をつけて複数 slot を渡せる
- 親から来た slot に対して、子で props を渡せる
動的コンポーネント
コンポーネントを動的に切り替えることが可能。
<!-- 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>
- https://ja.vuejs.org/guide/components/provide-inject.html
- 子に値を変更されたくないときは readonly
- inject にはデフォルト値を設定できる
- key が競合するときは Symbol が使用できる
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...