Open7

Vue3について調べる

kirikirimaikirikirimai

emitsについて

子コンポーネントから親コンポーネントに伝える方法。
例えばフォームの値を親のコンポーネントに伝える。

子コンポーネント側

子コンポーネントをForm.vueとして作成。

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と指定。任意の変数名で大丈夫。

kirikirimaikirikirimai

フォームのSubmitなどの処理を止めるには

フォームタグ で submit を した時に発生するデフォルトの動作を止めたい場合は以下のようにする。

<from @submit.prevent="submitForm">
<button type="submit">送信</button>
</form>

submit.preventというのがpreventDefaultの処理となる。

kirikirimaikirikirimai

Vue3にVuetifyをインストールしてみる

Vuetifyも使うらしいのでインストールから設定方法まで。

インストール

まずは既存のVue3のプロジェクトにインストールする

npm i vuetify

使えるようにする

インストールしただけでは使えないので設定する。

main.js
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')

上記のように必要なものをimportcreateVuetifyでオブジェクトを作成してuse()に指定する。

v-iconでアイコンを使えるようにする

まず、下記をインストール

npm install @mdi/font -D

その後、main.jsでインポートする

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と指定する。
その他、下記を参考
https://blog.takasqr.dev/ja/blog/vuetifyjs_v-icon

kirikirimaikirikirimai

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では使えないので注意

kirikirimaikirikirimai

リアクティブ変数に関して

リアクティブとは変数の変更を検知すること。
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

上記のようにauthortitleを分割代入するとリアクティブが失われるので、どうしても分割代入を利用したい場合はtoRefs関数を使うようにする。その場合は、valueで参照することになるので気を付ける。

let {author,title} = toRefs(book)

title.value
kirikirimaikirikirimai

ボタンなどのコンポーネント化

ボタンとか使いまわす場合にコンポーネント化する際の、Reactとの違い。
一旦、完成版。

BaseButton.vue
<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 />と閉じタグを入れてしまってよい。

実際に親要素で使用する場合は以下のようにすればいい。

Parent.vue
<template>
 <BaseButton color="green" @:clickHandler="addTodo">追加</BaseButton>
</template>
<script setup>
const addTodo = ()=>{
 console.log("addTodo")
}
</script>

CSSについて

CSSでボタンの色も変更できるようにしたいと思う。
がその場合defineProps colorを文字列として指定して、buttonタグに:classで指定しておく。
*クリックイベントは消してあります。

BaseButton.vue
<script setup>
 const props = defineProps({
     color:String
 });
</script>
<template>
 <button class="btn" :class="props.color">
        <slot>ボタン名</slot>
  </button>
</template>

これを親からは属性にcolor="green"と指定すれば、BaseButtonコンポーネントにあるgreenクラスが適用される。

Parent.vue
 <BaseButton color="green" >追加</BaseButton>

クリックイベントについて

この方法がいいのか分からないがあくまで一例として。

BaseButton.vue
<script setup>
 const props = defineProps({
     clickHandler:Function
 });

const clickHanlder = ()=>{
  props.clickHandler
}

</script>
<template>
 <button @click="clickHandler">
        <slot>ボタン名</slot>
  </button>
</template>

CSSと同様にdefinePropsFunction型としてclickHandlerを指定して。
BaseButton.vue内でのclickHandlerの処理でprops.clickHandler()として呼び出す。

親のコンポーネントからは以下のように:clickHandlerに実行したい関数をしていする。

Parent.vue
 <BaseButton :clickHandler="addTodo">追加</BaseButton>

<script setup>
const addTodo = ()=>{
 console.log("addTodo")
}
</script>

クリックイベント(emit)

あとはいつも通りのemitによる、親要素へのイベントの処理。
子要素となるBaseButton.vuedefineEmitsで設定する。

BaseButton.vue
<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を指定。また引数の値も親要素に渡せるので、もし必要であれば指定する。

あとはdefineEmitson-clickとして、clickHnadler内でemitを呼び出す。
第2引数に渡したい文字列を設定して完了。

親のコンポーネントからは以下のように呼び出す。

Parent.vue
<template>
 <BaseButton @on-click="testFunc">テスト</BaseButton>
</template>

<script setup>
const testFunc = (str) => {
    alert(str);
};
</script>

emitで指定した@on-clickを設定して実行したいtestFuncを指定しています。
BaseButton.vueで設定した第2引数の値はtestFunc関数から取得できる。

kirikirimaikirikirimai

ディレクトリ構成について

vueのプロジェクトを作成する時に、vueRouterやPiniaを使用することを選択するとあらかじめsrc配下のディレクトリ構成も以下のようになる。

src/
├── assets
├── components
├── router
│        └── index.js
├── stores
│        └── counter.js
├── views
│        └── AboutViews.js