OWNED MEDIAウェブ制作に役立つコンテンツを発信中!

Vue.jsの2系でTypeScriptを使う「Options API」と「Class API」

最終更新日:2021.10.28 Update!!
Vue.jsでTypeScriptを使う場合、型指定を行う場合には少し書き方が異なり、2系の場合には「Options API」もしくは「Class API」を使う形になります。現在Vue.jsは3系もリリースされており「Composition API」というのも登場しますが、これについてはまた改めて別記事にまとめていきたいと思います。Vue.jsの最新版は3系になりますが、まだまだ2系も使われていることが多いかと思います、その中でTypeScriptに移行するというケースもあるようですので、ちょっと今更感はありますが今回は備忘録として残しておきたいと思います。   過去記事「webpack&TypeScriptのプロジェクトでVue.jsを使える開発環境を構築してみる」でもVue.jsでTypeScriptを使う環境構築についてまとめていましたが、詳しい書き方などはそこまで触れていなかったので、今回はその点を中心にまとめていきます。   まず、TypeScriptではなく通常のJavaScriptで記述する場合に、コンポーネントの基本の形としては下記のようになります。
<script>
  import { ChildComponent } from '~/components/ChildComponent';
  export default {
    name: 'ExampleComponent',
    components: {
      ChildComponent
    },
    props: {
      isDisplay: {
        type: Boolean,
        default: true
      }
    },
    data() {
      return {
        message: 'hello world!'
      }
    },
    computed: {
      messageConvert() => this.message.toUpperCase();
    },
    created() {
      this.displayMessage();
    },
    methods: {
      displayMessage() {
        if(this.isDisplay) {
          return this.messageConvert();
        } else {
          return false
        }
      }
    }
  }
</script>
  TypeScriptを使う場合にはここに型定義を追加していくのですが、そのままつけることができないので、次のいずれかの方法に変える必要があります。  
Vue.jsのオプションをベース記述できる「Options API」
「Options API」はVue.jsの拡張という位置付けで、元の記述をベースに型定義をそのまま追加できる形になります。メリットとしてはTypeScriptの移行コストや学習コストが低いというものがあります。もともとVue.jsに含まれる型チェックの機能を利用するため、vueをインポートし、Vue.extend()で継承するようにします。これでTypeScriptが有効になります。合わせてscriptタグのlang属性に「ts」と追加しておくのも忘れないようにします。
<script lang="ts">
  import Vue from 'vue'
  import { ChildComponent } from '~/components/ChildComponent';
  export default Vue.extend({
    name: 'ExampleComponent',
    components: {
      ChildComponent
    },
    props: {
      isDisplay: {
        type: Boolean,
        default: true
     }
    },
    data() {
      return {
        message: 'hello world!' as string
      }
    },
    computed: {
      messageConvert(): string {
        return this.message.toUpperCase();
      }
    },
    created() {
      this.displayMessage(this.messageConvert);
    },
    methods: {
      displayMessage(message: string): string | boolean {
        if(this.isDisplay) {
          return message;
        } else {
          return false
        }
      }
    }
  })
</script>
  このように、Options APIではVue.jsのオプションについてこれまでと同じように定義し、それぞれ変数や関数の返り値の型定義を行なっていきます。TypeScriptに慣れていない・詳しくない場合や、これまで通りのVue.jsの記法が良いという場合にはこの形の方が良さそうですね。ただ、型定義のエラーが結構出てしまう印象があり、どちらかというとこのあと紹介するクラスベースのClass APIの方が情報も充実しているのかなと思いますので、本格的に導入する場合には検討の余地がありそうという感じでしょうか。また次のクラスベースの書き方より少しコードが長くなったり見通しもあまり良く無いというのが気になります。  
クラスベースで記述できる「Class API」
「Class API」ではこれまでのVue.jsのオプションの書き方とは異なり、クラスを定義してエクスポートするような形になります。また、Vue.jsで使われているオプションに対応させるために@から始まるデコレーターというものを使います。このため、Class APIの場合には下記のモジュールを追加でインストールしておきます。
$ npm install --save vue-property-decorator vue-class-component
  モジュールがインストールできればクラスベースでVue.jsのtypescriptへの移行ができるようになります。下記のようにモジュールやコンポーネントをインポートして、クラスを定義していきます。その時にコンポーネントなどについてはデコレーターを使って記述していきます。コンポーネントは「@Component()」に続いて読み込むものを指定していきます。   Propsも同じで「@Prop()」のデコレーターを使って値を受け取ります。引数には受け取る値の型や初期値などの指定が入ります。その後続けて、受け取る値のキー名を型定義して指定します。その後、続けてVue.jsのオプションを記述していきます。
<script lang="ts">
  import { Component, Prop, Emit, Watch, Vue } from 'vue-property-decorator';
  import ChildComponent from '~/components/ChildComponent.vue';
  @Component({
    components: {
      ChildComponent
    }
  })
  export default class ExampleComponent extends Vue {
    @Prop({ type: Boolean, default: true })
    isDisplay?: boolean;
    message: string = 'hello world!';
    get messageConvert(): string {
      return this.message.toUpperCase();
    }
    created() {
      this.displayMessage(this.messageConvert);
    }
    displayMessage(message: string): string | boolean {
      if(this.isDisplay) {
        return message;
      } else {
        return false
      }
    }
  }
</script>
  各オプションですが、まずdataオプションについては、クラス内の変数としてキーと合わせて定義します。そして、computedに関してはgetアクセサに続いて関数を定義します。setterとして使う場合にはsetアクセサを使って同じように記述します。created()やmounted()などのライフサイクルフックはそのままのオプションの書き方で定義していき、methodsについてはクラス内の関数として個別に定義していきます。   これまでのオプションの書き方とは異なる部分がたくさんあり、慣れないうちはややこしく感じるのですが、見てわかる通り全体的にコードがすっきりとしているのがわかります。個人的には型定義関連のエラーで躓くことがOptions APIと比べると少ない印象があり、情報もこちらの方が多いため良く使われているのかなと思います。ただ、Vue3系ではこの方法が採用されていないようですが、、   上記以外にもWatchやmixin、middleware、またstore内の処理なども少し書き方が異なる部分があるのですが、それらについてはまた改めて別記事でまとめていきたいと思います。  
クラスベースの記述でコンポーネントが正しく読み込まれない
Class APIでの書き方で1点注意しておきたいのが、コンポーネントとして複数を読み込む際に、うまく表示されないという現象が起こることがありました。下記のように、親コンポーネント側で、子コンポーネントを個別に読み込んでいくのですが、なぜか全て同一のコンポーネントとして表示されてしまっている状態になっていました。
import ChildComponent1 from '~/components/common/ChildComponent1.vue'
import ChildComponent2 from '~/components/common/ChildComponent2.vue'
@Component({
  components: {
    ChildComponent1,
    ChildComponent2
  }
})
export default class ParentComponent extends Vue {
  ....
}
  子コンポーネント側(一部抜粋)ではこのようにコンポーネントを定義しており、その中では特にコンポーネントを呼び出すこともないので、@Component()のデコレーターも使わず記述している形でした。
// コンポーネント1
export default class ChildComponent1 extends Vue {
  ....
}

// コンポーネント2
export default class ChildComponent2 extends Vue {
  ....
}
  どうやらそれが原因だったようで、コンポーネント側では、そのコンポーネント内で読み込むものがない場合にも、@Component()のデコレーターでは空として記述しておく必要があるようです。こうすることで正しくコンポーネントが呼び出されるようになりました。
// コンポーネント1
@Component({})
export default class ChildComponent1 extends Vue {
  ....
}

// コンポーネント2
@Component({})
export default class ChildComponent2 extends Vue {
  ....
}
 
  今回はVue.jsでTypeScriptを使う場合の記述方法である「Options API」と「Class API」について基本的な部分をまとめてみました。今回まとめている以外にも知っておくべき知識もあるのですが、まずは基本的な部分は押さえておきたいですね。色々と覚えることが大変なので、やはり良く言われている通りVue.jsとTypeScriptはあまり相性が良くないのかなという印象を感じました、、どちらでもTypeScriptに対応はできるので、状況や優先事項に合わせて使い分けられるといいですね。   (参考にさせて頂いたサイト) Vue.js to TypeScriptの書き方一覧
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

SPONSORED

    KEYWORD SEARCH

    RECENT POSTS

    合同会社デザインサプライ -DesignSupply. LLC-

    サイト制作・開発 / 各種デザイン制作 / ウェブプロモーション企画

    合同会社デザインサプライ(DesignSupply. LLC)

    Office:大阪府大阪市天王寺区清水谷町3-22
    Email:info@designsupply-web.com
    • Twitter
    • Github
    CONTACT USSCROLL TO TOP
      • Facebook
      • Twitter
      • Github
      • Instagram