4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.js(ver 2.x/3.x)コンポーネント定義の方法整理してみた

Posted at

はじめに

ポートフォリオ制作真っ最中です。
コンポーネントの定義の仕方がたくさん出てきて混乱したので、備忘録の意味も含めて整理します。
初心者で同じところで混乱してしまった方に届けば嬉しいです。
先人の方々の知恵と公式の内容を引用し整理しています。
※誤りなどあれば、コメントくださると嬉しいです。

結論

以下によって、コンポーネントの定義の仕様が異なる。
※網羅性は担保できていないので、他にもこんなのがあるみたいなのがあれば、コメントいただければ嬉しいです。

バージョン(2.x or 3.x )
使用するグローバルAPI(Vue.component or Vue.extend)
コンポーネントの管理の仕方(単一ファイルで管理するか or not)
TypeScriptのサポートを使うか(型推論が必要か or not)
 ※使う場合、クラススタイルのVueコンポーネントの記述法となる
compositionAPIがを使うか(「ロジックの抽出と再利用」が必要か or not)

何が良い/悪いに関しては言及しません。
詳細を順を追って見ていきます。


バージョン2.x

コンポーネントの基本

app.js内にて
ローカル定義

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

グローバル定義

Vue.component('sample', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
<div id="sample">
  <button-counter></button-counter>
</div>

説明

  • 基本的には、ローカル定義が推奨される。理由はJavascriptのファイルサイズを小さくできるため。
    Webpack のようなビルドシステムを利用しているときに、グローバルに定義した全てのコンポーネントは、たとえ使用しなくなっても、依然として最終ビルドに含まれてしまう。
  • Vue.componentを使用し、コンポーネントを定義する
  • dataオプションは参照を制限するため関数で書く
  • templateで描画htmlを書く
  • ルート固有のelオプションは使えない

コンポーネントの詳細

  • 基本的にケバブケース(xxxx-xxxx)が用いられる。

  • パスカルケース(XxxxXxxx)も用いられるがDOM内で使用できないため、基本的にはケバブケース

  • ローカル定義されたコンポーネントは、他のサブコンポーネント内では使用できない

ComponentA を ComponentB 内で使用可能にする場合

componentオプションでサブコンポーネントを定義する


var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

ES2015モジュールを利用しているのならば、以下のように記載
ComponentAをインポートしている、export defaultで定義しているなどの点が異なる


import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

単一ファイルコンポーネント


<template>
 <div id="app">
  <h1>My Todo App!</h1>
   <TodoList/>
 </div>
</template>

<script>
import TodoList from './components/TodoList.vue'

export default {
 components: {
  TodoList
 }
}
</script>

説明

以下の3つのタグで構成される。

  • <template>
  • <script>
  • <style>

上からそれぞれ、描画するDOM、Vueインスタンス等の記述、cssを表す

vue.extendグローバルAPIのコンポーネント定義

グローバルAPIのvue.extendを用いたコンポーネントの定義の仕方


var sample = Vue.extend({
  template: '<h1>Sample</h1>'
})

vue.extend()とvue.componentの違い

Vue.extend()はクラス継承メソッド。Vueのサブクラスを作成し、コンストラクターを返す。

Vue.component()は、Vue.directive()およびVue.filter()に似たアセット定義メソッド。Vue.jsがテンプレートで指定されたコンストラクターをストリングIDに関連付ける。実はオプションをVue.component()に直接渡すと、内部でVue.extend()を呼び出している。

クラススタイルのコンポーネント定義(TypeScriptの型推論を使用する場合)

公式にメンテナンスされているvue-class-componentのデコレータをimportし定義する
また、アノテーション(@;関連データの注釈のコード)を付与する

import Vue from 'vue'
import Component from 'vue-class-component'

// @Component デコレータはクラスが Vue コンポーネントであることを示します
@Component({
  // ここではすべてのコンポーネントオプションが許可されています
  template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
  // 初期データはインスタンスプロパティとして宣言できます
  message: string = 'Hello!'

  // コンポーネントメソッドはインスタンスメソッドとして宣言できます
  onClick (): void {
    window.alert(this.message)
  }
}

バージョン3.x

前提

コンポーネントの定義以外もバージョン2.xとは諸々異なる。
今回は後述のコンポーネントの定義に必要なVueインスタンスの生成の相違点のみ説明する

Vueインスタンスの生成


// バージョン3.x

Vue.createApp({
  data() {
    return {
        xxxx: 'sample',
    }
  }
})

// バージョン2.x

var vm = new Vue({
  el: '#example',
  data: {
    xxxx: 'sample'
  },
})

コンポーネントの基本

app.js内にて

ちなみに、本セクションの仕様(2.xでのコンポーネント定義方法)はOptionAPIと呼ばれる
3.xにて新しく導入されたCompositionAPIとよく比較される

ローカル定義


var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }

new Vue({
const app = Vue.createApp({
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

componentsオプションの使用などバージョン2.xと殆ど変わらない。
Vueインスタンスの生成のみ異なる。

グローバル定義


// Vue アプリケーションを作成
const app = Vue.createApp({})

// グローバルな button-counter というコンポーネントを定義します
app.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
})
<div id="sample">
  <button-counter></button-counter>
</div>

説明

  • <2.xと同様>基本的には、ローカル定義が推奨される。理由は2.xと同様
  • 作成したアプリケーションappのコンポーネントappを使ってapp.componentとし、コンポーネントを定義する
  • dataオプションは2.xでは関数で書く仕様だったが、3.xはデフォルトで関数で書くようになっている
  • <2.xと同様>templateで描画htmlを書く
  • マウントする場合は、app.mount('#{id}')でidを指定しマウントする

コンポーネントの詳細

  • <2.xと同様>基本的にケバブケース(xxxx-xxxx)が用いられる。

  • <2.xと同様>パスカルケース(XxxxXxxx)も用いられるがDOM内で使用できないため、基本的にはケバブケース

  • <2.xと同様>ローカル定義されたコンポーネントは、他のサブコンポーネント内では使用できない

ComponentA を ComponentB 内で使用可能にする場合

2.xと全く同じのため割愛

単一ファイルコンポーネント


<template>
  <div class="xxxx">
  </div>
</template>

<script>
import xxxx from "./xxxx.vue"

export default {
  components: {
    TodoItem
  },

  data() {
    return {
    }
  },

  methods: {
  }
}
</script>

dataオプションの書き方が少し変わりますが、それ以外は全く変わらない。

defineComponent グローバル API

2.xのvue.extendと同じような位置づけでコンポーネントを定義できます。


const sample = Vue.defineComponent({
  template: '<h1>Sample</h1>'

Composition API

3.xにて新しく導入されたComposition APIなので詳細に書きます。

「”なぜ” Composition APIを使うのか」
その理由は、
「ロジックの抽出と再利用をするため」

とのこと。

「”なぜ” ロジックの抽出と再利用をするのか」
その理由は、
「複雑に肥大化したコンポーネントを、小分けにして関心事で分別し、クリーンな状態に整理ため」

「結論」コードの整理により可読性、メンテナンス性、柔軟性を高めるためComposition APIを使用するとのことです。

compositionAPIが特にわからなかったので、こちらの記事を参考にさせていただきました。
ありがとうございます!

なぜ、Vue Composition APIを使うのか、理解する【メリット/デメリットまとめ】

具体的な書き方

Composition API特有のオプションを確認します。

setupオプション

コンポーネントが作成される前にpropsが解決されると実行され、コンポジション API のエントリポイントとして機能。
setup が実行されたときは、まだコンポーネントのインスタンスが作られないため、setup オプションの中では this を使用できません。アクセスできるプロパティはprops``context(attrs, slotsm, emit)のみです。つまり前述したようなプロパティ(data, computed, methods)にはアクセスできません。

setup関数は2つの引数を取ります。第一引数はprops引数、第二引数は context引数です。
まずsetup関数内のpropsはリアクティブで、新しいpropsが渡されたら更新されます。

具体的なコード


// MyBook.vue

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

次にcontextは非リアクティブです。参照をいじりたくない場合に使用します。

具体的なコード


// MyBook.vue
export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}

詳細はこちらの記事を参考にしてもらえればと思います。
Vue 2.xのOptions APIからVue 3.0のComposition APIへの移行で知っておくと便利なTips

TypeScriptのサポート

TypeScriptで適切に型推論を行うために前述したdefineComponentグローバルAPIを用いて、コンポーネントを定義する必要があります。

これにより型推論が有効化されます。


import { defineComponent } from 'vue'

const Component = defineComponent({
})

また、OptionAPIでもCompositionAPIでもともに利用することができます。
2.xでは、クラススタイルのコンポーネント定義を行いましたが、それをdefineComponentの定義に変えることで使用できます。

以下の例ではtypeを使用し型推論を定義しています。

import { defineComponent, PropType } from 'vue'

interface ComplexMessage {
  title: string
  okMessage: string
  cancelMessage: string
}
const Component = defineComponent({
  props: {
    name: String,
    success: { type: String },
    callback: {
      type: Function as PropType<() => void>
    },
    message: {
      type: Object as PropType<ComplexMessage>,
      required: true,
      validator(message: ComplexMessage) {
        return !!message.title
      }
    }
  }
})

以下はCompositionAPIと併用した例です。
同様にdefineComponentAPIを使用します。


import { defineComponent } from 'vue'

const Component = defineComponent({
  props: {
    message: {
      type: String,
      required: true
    }
  },

  setup(props) {
    const result = props.message.split('') 
    const filtered = props.message.filter(p => p.value) 
  }
})

以上、個人的にまとめましたが、多々表記ゆれや誤りなども含みうると思いますので、ご指摘貰えれば嬉しいです。

■参考にさせてもらった記事

https://optimizely.github.io/vuejs.org/guide/composition.html
https://jp.vuejs.org/v2/guide
https://qiita.com/karamage/items/7721c8cac149d60d4c4a
https://techblog.zozo.com/entry/vue-options-api-to-composition-api
https://qiita.com/karamage/items/7721c8cac149d60d4c4a

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?