シナプス技術者ブログ

シナプスの技術者公式ブログ。インターネットで、鹿児島の毎日を笑顔にします。

Vue.js 3 のコンポーネントについて

こんにちは、システム開発課の今門です。今回は、Vue.jsについて書きます。

Vue.jsのバージョンをめぐる現状

シナプスでは、新規のWebサイトを構築する際に、Nuxt.jsを利用する機会が増えています。
Nuxt.jsは、ご存知のとおり、Webアプリ開発を効率よく開発できるVue.jsをベースとしたJSフレームワークです。
ベースとなっているこのVue.js、バージョンとしては2系(Vue.js 2)と3系(Vue.js 3)が併存している状態です。
npm install vueを実行すると今やVue.js 3がインストールされるのでこちらがデフォルトと言えますが、node modulesの互換性の問題であったり、 Nuxt.jsに組み込まれているのがVue.js 2であったりすることから、まだまだVue.js 3を取り入れている開発現場は少ないように思います。

しかし、それらは時間が解決する問題であり、近い将来には、Vue.js 3が主流となることは明らかです。ということで、社内でもVue.js 3での開発に向けて準備を進めている最中です。
今回は、JSフレームワークの主要な技術要素のひとつ「コンポーネント」について、Vue.js 3での特徴を紹介したいと思います。

コンポーネントの記載方法

グローバル・コンポーネントはインスタンスの外ローカル・コンポーネントはインスタンンスの中で登録するというルールは、Vue.js 3でも同じです。
ただし、Vue.js 2とVue.js 3ではインスタンスの記述方法から異なっています。

Vue.js 2では次のように、vue関数で新しいvueインスタンスを作成していました。

const app = new Vue({

   el: '#app',
  // オプション

})


Vue.js 3では、次のようにcreatApp関数を使います

const app = Vue.createApp({

   //オプション

}).mount('#app')


グローバル・コンポーネント

グローバル・コンポーネントは、次のように記載します。

// 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>`
});
app.mount('#app');


または次のように記載することも可能です。

Vue.createApp({})
.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
})
.mount('#app');


ローカル・コンポーネント

次のようにインスタンスの中に記載します。
ローカル・コンポーネントは、基本的にVue.js 2 と同様の記載方法になります。

const app = Vue.createApp({
  props: ['name', 'address', 'tel' ],
  components: {
    'staff-profile': { 
        template:
            `
                <li v-text="name"></li>
                <li v-text="address"></li>
                <li v-text="tel"></li>
            `
    }
  }
})


Vue.js 2からの変更点

上記のローカル・コンポーネントstaff-profileが、親コンポーネントからどのように呼び出されるかは容易に想像がつきます。

<ul>
    <staff-profie></staff-profile>
</ul>


当たり前の指定に思えますが、このごくシンプルな指定が可能になったのは、実はVue.js 3になってからです。Vue.js 2までは、コンポーネントのテンプレートは、ひとつのルート要素しか持つことができませんでした。
複数の要素(ここではli要素)を扱う場合には、2系では次のようにhtmlの文法に従って親要素で囲むことになります。

//Vue.js 2 のコンポーネントでルートをひとつにする(1)

const app = new Vue({
  el: '#app',
  props: ['name', 'address', 'tel' ],
  components: {
    'staff-profile': { 
        template
            `
                <ul>
                    <li v-text="name"></li>
                    <li v-text="address"></li>
                    <li v-text="tel"></li>
                </ul>
            `
            // 呼び出し元から、ul要素は削除することになります。
    }
  }
})

//Vue.js 2 のコンポーネントでルートをひとつにする(2)
//あるいは強引にtemplate要素を使ってみるとか・・

const app = new Vue({
  el: '#app',
  props: ['name', 'address', 'tel' ],
  components: {
    'staff-profile': { 
        template
            `
                <template>
                    <li v-text="name"></li>
                    <li v-text="address"></li>
                    <li v-text="tel"></li>
                </template>
            `
    }
  }
})


小さな変更ですが、このような例を見ると、Vue.js 3になってより要素をコントロールしやくすくなったことがわかっていただけるかと思います。


Composition API

ここまで、Options APIを前提にコンポーネントの書き方について説明してきましたが、Vue.js 3へのバージョンアップの中心となるのは、Composition APIの標準搭載になります。Vue.js 2でもプラグインとして提供されていたのですが、標準搭載を機に、Options APIからComposition APIに移行しようと考えている方も多いのではないでしょうか。
Composition APIについて、簡単に説明したいと思います。

Options APIの問題点

Options APIは、コンポーネントのオプション (data, computed, methods, watchなど) ごとにロジックをまとめて書きます。その書き方は非常に見通しがよく、このコンポーネントが何をするのかをわかりやすくします。そのわかりやすさが、Vue.jsの人気の原因でもあります。
しかし、コンポーネントの規模が大きくなり、複数の処理を実行するようになると、このオプションごとにロジックをまとめるということが足枷になってしまうことがあります。
つまり、処理ごとのデータやコードが分散してしまいがちになってしまいます。 そうなると、処理ごとにデータやコードを共通化して、外部から呼び出すといった効率化も難しくなってしまいます。

例えば、下記のようなケースです。

// Options APIの例。処理毎にデータや関数がまとまっておらず、オプション間で区切られている。

const app = Vue.createApp({});

app.component('my-comp', {
  data() {
    return {
      variable1: 0,  //処理1で使うデータ
      variable2: 0,  //処理2で使うデータ
      variable3: 0  //処理3で使うデータ
    }
  },
  method: {
      func1() {・・・},   //処理1で使う関数
      func2() {・・・},  //処理2で使う関数
      func3() {・・・}   //処理3で使う関数
  },
  template: `
    <div>・・・</div>`
});
app.mount('#app');


Composition API の基本構成

Composition API を利用することで、Options APIが持つ問題点を解決することができます。
Composition APIの基本構成は、次のようになります。

Vue
.createApp({})
.component('my-comp', {

    // (1) プロパティを定義する
    props: [ 'msg' ],

    // (2) テンプレートを定義する
    template: `<div>・・・</div>`

    // (3) データや関数を定義する
    setup(props) {
   ・・・
    }


})
.mount('#app');


コンポーネントの内容は、大きく3つに別れます。
ひとつめは、親データから渡されたデータを受け取るpropsの定義です。
2つめは、templateのフォーマット定義です。
そして3つめは、データや関数など、ライフサイクルフックの定義になります。これらはsetup() に自由に配置できるので、処理毎にまとめて、共通化/外部読み込みができるようになります。

setup()内での定義内容/形式をいくつか紹介します。
Options APIと対応させるとわかりやすいかと思います。

/* 
【データオブジェクトの定義】
 ・Options APIのdataオプションに相当
 ・refメソッドを使う #その他reactiveメソッドも利用可
*/
const variable = Vue.ref( props.msg )

/* 
【イベントハンドラーの定義】
 ・Options APIのmethodsオプションに相当
 ・関数定義
*/
const func = function() {・・・}

/* 
【算出プロパティの定義】
 ・Options APIのcomputedオプションに相当
 ・computed メソッドを使う
*/
const compd = Vue.computed(function() {・・・})

/* 
【定義内容のまとめ/オブジェクト形式で返す】

*/
return {
    variable, func, compd
}


Compositon APIで処理ごとにデータや関数をまとめる

次のようにComposition APIを記載することで、処理ごとにデータやコードを共通化して、外部から呼び出すといった効率化も行いやすくなります。

Vue
.createApp({})
.component('my-comp', {

    props: [ 'msg1', 'msg2', 'msg3' ],
    template: `<div>・・・</div>`

    setup(props) {

// 処理1に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable1 = Vue.ref( props.msg1 )
  const func1 = function() {・・・}
  const compd1 = Vue.computed(function() {・・・})

// 処理2に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable2 = Vue.ref( props.msg2 )
  const func2 = function() {・・・}
  const compd2 = Vue.computed(function() {・・・})

// 処理3に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable3 = Vue.ref( props.msg3 )
  const func3 = function() {・・・}
  const compd3 = Vue.computed(function() {・・・})

    }

    return {

        variable1, func1, compd1,
        variable2, func2, compd2,
        variable3, func3, compd3

    }


})
.mount('#app');


たとえば、処理1に関するまとまりを、外部ファイル化( common1.js )するには次のように記載します。

function common1(msg1) {

    const variable1 = Vue.ref( props.msg1 )
    const func1 = function() {・・・}
    const compd1 = Vue.computed(function() {・・・})

    return {
        variable1, func1, compd1
    }

 }


呼び出し元のreturn内で外部ファイル内の関数を指定します。

    return {

        common1(msg1),
        variable2, func2, compd2,
        variable3, func3, compd3

    }

まとめ

シンプルでわかりやすいけれども、大規模開発には向かないのではないかと言われてきたVue.jsですが、Composition APIの標準採用により、これまでの親しみやすさは残しつつ、本格的なWebアプリ開発にも十分対応できるようになっていることをおわかりいただけたのではないでしょうか。実際の現場では、開発規模の大きさなどにより、Options APIとComposition APIを使い分けていくことになると思います。

より詳しい情報は、(このブログを書くのにも大いに参考にさせてもらった) Vue.js の公式サイト(Vue.js 3版)をご参照ください。ここまで豊富な公式の情報が、すべて日本語化されているのは、非常にありがたいです。

興味を持った方は、ぜひVue.js 3 を使ってみてください。