😺

Vue コンポーネントについて①

2024/11/10に公開

今回はコンポーネントの理解を深めます。長くなってしまうので2回に分けます。

まず、コンポーネントはパーツ(部品)という意味で、このパーツを沢山作ってそれをそれぞれいい感じに配置させて設計し、アプリケーションを作成します。
 その前提としてコンポーネントのデータが集まっている時のデータ構造はHTMLと同じような感じでツリー構造になっています。(コンポーネントツリー)そして一番上のコンポーネントはルートコンポーネントと言います。

このルートコンポーネントに入るのはmain.jsの中のcreateApp(App)に入れるApp.vueがルートコンポーネントになります。

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')

ツリー状になっているので、とあるコンポーネントから見た上のコンポーネントは親コンポーネント、下のコンポーネントは子コンポーネントとなります。

では実際にコンポーネントを作成して繋げていきます。
ツリー構造なのでApp.vueの下にコンポーネントを置き、仮にCountUp.vueというファイルを作成します。
そしてAppコンポーネントの<script setup>にデフォルトインポートし、<template>タグ内で

<script setup>
import CountUp from './CountUp.vue'
</script>

<template>
  <CountUp />
</template>

このように使うとCountUp.vueのファイルデータが反映されます。
コンポーネントが埋め込まれるような感じですね。
これでCountUp.vueに書き込むと画面に反映されます。

<template>
  <div><h2>CountUp++</h2></div>
</template>


このAppで書かれた<CountUp />は<h2>タグで書かれたのと同じ意味になります。

Vueではこのようにコンポーネントをインポートしてそれをタグとして<template>に埋め込むことでコンポーネント同士を繋げます。
もちろん、埋め込まれているだけなのでApp.vueに書き込んでも反映されます。

<template>
  <h1>App</h1>
  <CountUp />
</template>


あくまでも、<CountUp />のところだけがCountUp.vueの<template>の中身に置き換わるだけです。
検証ツールを見ても

このように埋め込まれているのがわかるかと思います。
CountUp.vueに <p>count:</p>を追加しても反映されます。
 で、このコンポーネントタグは複数書くことができ、使い回しができます。

<template>
 <h1>App</h1>
 <CountUp />
 <CountUp />
 <CountUp />
</template>


もちろん、3つ全て同じものが出ますので簡単に使い回しができます。
CountUp.vueの<script setup>に処理があったらこれも実行されます。

<script setup>
console.log('script')
</script>

<template>
  <div>
    <h2>CountUp++</h2>
    <p>count:</p>
  </div>
</template>


このようなCountUpボタンも3つ分できます。

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

const count = ref(0)
</script>

<template>
  <div>
    <h2>CountUp++</h2>
    <p>count:{{ count }}</p>
    <button @click="count++">+1</button>
  </div>
</template>


 それぞれが独立した処理になっています。
ちなみに親から子コンポーネントにあるカウントにはアクセスできませんし、逆に子から親にデータのアクセスはできません。
コンポーネントタグには2つの書き方があり、<CountUp />か<CountUp></CountUp>の閉じタグありのどちらかになります。

コンポーネントの名前ですが、CountUpのように文字の先頭を大文字にして区切りも大文字にするパスカルケースが推奨されています。
もう一つ推奨されているのはコンポーネントの名前を2単語以上にするというのがあります。
1単語だと他のHTMLのタグとかぶってしまう可能性があるからです。

次にimportしなくてもコンポーネントが使えるようになる、グローバル登録を解説します。
例えば、BaseIcon.vueのコンポーネントを作ります。
内容はアイコンなので適当に

<template>😾</template>

とし、いろいろなコンポーネントで使用したいとします。
その場合に毎回このBaseIcon.vueをインポートするのは面倒なのでVueでは毎回インポートせずに済むように使えるような機能があります。
それはmain.jsにimportしてapp.mount('#app')が呼び出される前に
app.component('BaseIcon', BaseIcon)を書きます。

import { createApp } from 'vue'
import App from './App.vue'
import BaseIcon from './BaseIcon.vue'

const app = createApp(App)
app.component('BaseIcon', BaseIcon)
app.mount('#app')

こうすることでAppがルートコンポーネントになっているコンポーネントツリーの中であればどこでも自由にBaseIconをimportしなくても利用できます。

<script setup>
import CountUp from './CountUp.vue'
</script>

<template>
  <h1>App</h1>
  <BaseIcon />
  <CountUp />
  <CountUp />
  <CountUp />
</template>


これがコンポーネントのグローバル登録です。
app.component('BaseIcon', BaseIcon)この左側の第一引数の名前はなんでもいいのですが、ファイル名と同じがいいでしょう。

また、もし複数コンポーネントをグローバル登録したい場合は連続して書いてもいいですが、

import { createApp } from 'vue'
import App from './App.vue'
import BaseIcon from './BaseIcon.vue'

const app = createApp(App)
app
 .component('BaseIcon', BaseIcon)
 .component('BaseIcon', BaseIcon)
 .component('BaseIcon', BaseIcon)
 .component('BaseIcon', BaseIcon)
app.mount('#app')

このように繋げて書くこともできます。
 複数のコンポーネントを使用する際は指定するといいでしょう。
このグローバル登録は便利な一方、vuefアイルだけ見た時にいきなりimportしてないコンポーネントが登場したりするので若干わかりにくくなります。
 保守性の観点からするとグローバル登録はあまり良くないので基本的には毎回コンポーネントをimportして使うのがいいかと思います。
グローバル登録は余程頻繁に使用するコンポーネントにのみ使用した方がいいでしょう。

では、フォルダが増えてきたのでまとめたいと思います。
コンポーネントをまとめるときは一般的にcomponentsというフォルダを作って、そこにApp.vue以外の全てのコンポーネントを入れます。
移動させたらパスが変わるので忘れずに変更します。


こんな感じです。

あと、パスの一番先頭は.になってますがvite.config.jsを見ると@を使用することが可能です。この@には./srcという意味になっているのがわかるかと思います。

export default defineConfig({
  plugins: [vue(), vueDevTools()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

なのでこのように変更できます。

<script setup>
import CountUp from '@/components/CountUp.vue'
</script>

この@はviteの設定がないと使えないのでこれも注意点です。

次回はコンポーネントタグにidやclassの属性をつける方法などを紹介していきます。

Discussion