Vue3について調べる
emitsについて
子コンポーネントから親コンポーネントに伝える方法。
例えばフォームの値を親のコンポーネントに伝える。
子コンポーネント側
子コンポーネントをForm.vue
として作成。
<template>
<form @submit="submitForm">
<input type="text" v-model="city" placeholder="Enter city name" />
<button type="submit">Get Weather</button>
</form>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const city = ref('')
const submitForm = () => {
emits('submit-form', city.value)
}
const emits = defineEmits(['submit-form'])
</script>
重要なのがdefineEmits
で引数は配列で親に受け渡すイベント名を指定する。今回でいえばsubmit-form。
ここは指定しなくても動くが、指定しないとミスやメンテナンスがめんどくさくなるので指定しる。
名前の付け方は、例えばsubmitForm
という関数の場合はsubmit-form
など似た名前にすること。
実際に親に送るイベントは子コンポーネントで関数(submitForm
)をクリックしたらemits
の引数に親で利用するイベント名と渡したい値を指定する。
const submitForm = () => {
emits('submit-form', city.value)
}
親のコンポーネント
親はとりあえず、App.vue
とした。
実際に親のコンポーネントで利用する場合は以下のようにする。
子コンポーネントである<Form />
にemits
で指定したイベント名:@submit-form
と指定して、実際に処理したい関数を実行(getWeather
)。
<template>
<Form @submit-form="getWeather" />
</template>
<script setup lang="ts">
import Form from './components/Form.vue'
const getWeather = (city) => {
//処理したい内容
}
</script>
子コンポーネントから受け取った値を親で利用するには
子コンポーネントで下記のようにcity.value
というフォームの値を送っている。
const submitForm = () => {
emits('submit-form', city.value)
}
これを親のコンポーネントで利用する場合は、処理する関数の引数でそのまま利用できる。
const getWeather = (city) => {
constolo.log(city)
}
</script>
引数にcity
と指定。任意の変数名で大丈夫。
フォームのSubmitなどの処理を止めるには
フォームタグ で submit を した時に発生するデフォルトの動作を止めたい場合は以下のようにする。
<from @submit.prevent="submitForm">
<button type="submit">送信</button>
</form>
submit.prevent
というのがpreventDefault
の処理となる。
Vue3にVuetifyをインストールしてみる
Vuetifyも使うらしいのでインストールから設定方法まで。
インストール
まずは既存のVue3のプロジェクトにインストールする
npm i vuetify
使えるようにする
インストールしただけでは使えないので設定する。
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
const vuetify = createVuetify({
components,
directives,
})
app.use(vuetify)
app.mount('#app')
上記のように必要なものをimport
しcreateVuetify
でオブジェクトを作成してuse()
に指定する。
v-iconでアイコンを使えるようにする
まず、下記をインストール
npm install @mdi/font -D
その後、main.js
でインポートする
import '@mdi/font/css/materialdesignicons.css';
あとはv-icon
で囲って使いたいアイコンを指定する。
使いたいアイコンをmdi-cloud-download-outline
などmdi
から始まる英文字で指定するっぽい。
<v-icon>mdi-cloud-download-outline</v-icon>メニューその3
たとえばTwitterのアイコンならmdi-twitter
と指定する。
その他、下記を参考
templateについて
今回 の v-if の 対象 が button タグ ひとつ でし た が、 これ が 複数 要素( タグ) の 場合 は どう なる の でしょ う か。 たとえば、 以下 の よう な 感じ です。 isEditRef が true の 場合 に 1、 2 を 表示、 それ 以外 の 場合 は 3、 4 を 表示 し たい 場合 です。
<div v-if="isEdit">
<p>1</p>
<p>2</p>
</div>
<div v-else>
<p>3</p>
<p>4</p>
</div>
ただこの為だけの<div>
タグであればtemplate
を利用することができる。
<template v-if="isEdit">
<p>1</p>
<p>2</p>
</template>
<template v-else>
<p>3</p>
<p>4</p>
</template>
もちろんTemplateタグはhtml上では表示されません。
ただし、v-if``v-else
のみでv-showでは使えないので注意
。
リアクティブ変数に関して
リアクティブとは変数の変更を検知すること。
ReactでいうとuseState
の変数のような?ものという認識でいる。
Vueにおいては2つある。
プリミティブな値
単体の変数というのか、、、以下のように宣言と値の設定になる。
//宣言
const counter = ref(0)
//値の設定
counter.value ++
オブジェクト型のリアクティブ
他に、reactive
関数でオブジェクトみたいに扱うものがある。
//宣言
const state = ractive({
counter:0
})
//値の設定
state.counter++
オブジェクト型を分割代入する場合の注意点
分割代入するとリアクティブが失われる。
const book =reactive({
author:"test",
year:"2024",
title:"book name",
price:1980
})
let { author,title}=book
上記のようにauthor
とtitle
を分割代入するとリアクティブが失われるので、どうしても分割代入を利用したい場合はtoRefs
関数を使うようにする。その場合は、value
で参照することになるので気を付ける。
let {author,title} = toRefs(book)
title.value
ボタンなどのコンポーネント化
ボタンとか使いまわす場合にコンポーネント化する際の、Reactとの違い。
一旦、完成版。
<script setup>
import { defineProps } from "vue";
const props = defineProps({
clickHandler: Function,
color:String
});
const clickHandler = () => {
props.clickHandler();
};
</script>
<template>
<button class="btn" :class="props.color" @click="clickHandler">
<slot>ボタン名</slot>
</button>
</template>
<style scoped>
.btn{
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
color: #fff;
background-color: #2196f3;
transition: .3s;
}
.green {
background-color: #4caf50;
}
</style>
見ての通り、ベースとなるCSSの他、クリックの処理などはどういった処理がくるかわからないので、親からclickHandler
とう名前で指定した関数を処理するようにさせている。
またボタンを「追加」「削除」などボタンのテキストが変わることを想定して<slot>
というタグを入れている。
このslot
にはデフォルトでボタン名と入れているがもしデフォルトの値を入れないのであれば、<slot />
と閉じタグを入れてしまってよい。
実際に親要素で使用する場合は以下のようにすればいい。
<template>
<BaseButton color="green" @:clickHandler="addTodo">追加</BaseButton>
</template>
<script setup>
const addTodo = ()=>{
console.log("addTodo")
}
</script>
CSSについて
CSSでボタンの色も変更できるようにしたいと思う。
がその場合defineProps
でcolor
を文字列として指定して、button
タグに:class
で指定しておく。
*クリックイベントは消してあります。
<script setup>
const props = defineProps({
color:String
});
</script>
<template>
<button class="btn" :class="props.color">
<slot>ボタン名</slot>
</button>
</template>
これを親からは属性にcolor="green"
と指定すれば、BaseButtonコンポーネントにあるgreen
クラスが適用される。
<BaseButton color="green" >追加</BaseButton>
クリックイベントについて
この方法がいいのか分からないがあくまで一例として。
<script setup>
const props = defineProps({
clickHandler:Function
});
const clickHanlder = ()=>{
props.clickHandler
}
</script>
<template>
<button @click="clickHandler">
<slot>ボタン名</slot>
</button>
</template>
CSSと同様にdefineProps
にFunction
型としてclickHandler
を指定して。
BaseButton.vue
内でのclickHandler
の処理でprops.clickHandler()
として呼び出す。
親のコンポーネントからは以下のように:clickHandler
に実行したい関数をしていする。
<BaseButton :clickHandler="addTodo">追加</BaseButton>
<script setup>
const addTodo = ()=>{
console.log("addTodo")
}
</script>
クリックイベント(emit)
あとはいつも通りのemit
による、親要素へのイベントの処理。
子要素となるBaseButton.vue
にdefineEmits
で設定する。
<script setup>
const emit = defineEmits(["on-click"]);
const clickHandler = (str) => {
emit("on-click",str);
};
</script>
<template>
<button class="btn" @click="clickHandler('子要素からのイベント')">
<slot>ボタン名</slot>
</button>
</template>
通常のクリックイベントとして、clickHandler
を指定。また引数の値も親要素に渡せるので、もし必要であれば指定する。
あとはdefineEmits
でon-click
として、clickHnadler
内でemit
を呼び出す。
第2引数に渡したい文字列を設定して完了。
親のコンポーネントからは以下のように呼び出す。
<template>
<BaseButton @on-click="testFunc">テスト</BaseButton>
</template>
<script setup>
const testFunc = (str) => {
alert(str);
};
</script>
emit
で指定した@on-click
を設定して実行したいtestFunc
を指定しています。
BaseButton.vue
で設定した第2引数の値はtestFunc
関数から取得できる。
ディレクトリ構成について
vueのプロジェクトを作成する時に、vueRouterやPiniaを使用することを選択するとあらかじめsrc配下のディレクトリ構成も以下のようになる。
src/
├── assets
├── components
├── router
│ └── index.js
├── stores
│ └── counter.js
├── views
│ └── AboutViews.js