見出し画像

【Vue.js】なぜcomponentではdataプロパティは関数である必要があるのか?

はじめに

こんにちは、SHIFT の開発部門に所属しているKatayamaです。今期から転属になり、開発を担当していくことになりました。

今回はフロントエンドに関する記事を書いていこうと思います。 Vue.jsではいくつか仕様上、実装時には注意が必要なものがありますが、今回はその中でもdataプロパティに関してその仕様上の注意を見ていきたいと思います。

※なお以下は Vue.js 2.x に関する内容であり、Vue.js3.x では常に data プロパティは関数で書く必要があり、以下のように object で書いてしまい期待通りにならないという事は発生しない(Migration Build  『Data オプション』の項を参照。)

※以下に出てくる実装では、気軽にVue.jsを導入する方法としてCDNからの読み込みを採用している。

なぜコンポーネントでは data プロパティは関数である必要があるのか?

Vue.componentのVue インスタンスに基づいて作成された DOM では、各 DOM の Vue インスタンスのプロパティは全て同じメモリ上にあるものを参照する( 変数にオブジェクトを代入すると、オブジェクトそのものが変数に格納されるのではなく、メモリのポインタが格納され、複数の変数で同じオブジェクトを見てしまうという参照渡しと同じ)。

これは効率よく同じものを描画するための Vue の仕組みだが、この Vue の仕様ゆえに data プロパティではオブジェクトではなく関数で書いてあげる必要がある。

仮にオブジェクトで書くと以下のように、同じ Vue インスタンスを参照している DOM 全てでデータが書き換わってしまう。


動画のソースコードは以下。

<body>
    <div id="app">
        <my-component></my-component>
        <my-component></my-component>
        <my-component></my-component>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        const data = {
            number: 12
        }

        Vue.component('my-component', {
            data: function () {
                return data
            },
            template: '<p>いいね({{number}})<button @click="increment">+1</button></p>',
            methods: {
                increment: function () {
                    this.number += 1;
                }
            }
        })

        new Vue({
            el: '#app',
        })
    </script>
</body>

上記のようにならないようにするために、data プロパティは関数で書く必要がある。

※実際には上記も

data: function () {
   return data
}

となっているので関数に見えるが、return data; となっているdataが別の所で作成されたオブジェクトなので、return data;でtemplate上の {{ number }} に渡るのはオブジェクトのメモリの参照先(ポインタ)であり、number: 12 ではないため上記の動画のような挙動になっている。

正しくは以下のようなコードにする必要がある。

sample.html<body>
    <div id="app">
        <my-component></my-component>
        <my-component></my-component>
        <my-component></my-component>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        Vue.component('my-component', {
            data: function () {
                return { number: 12 }
            },
            template: '<p>いいね({{number}})<button @click="increment">+1</button></p>',
            methods: {
                increment: function () {
                    this.number += 1;
                }
            }
        })

        new Vue({
            el: '#app',
        })
    </script>
</body>

こうすると以下のように期待通りの動きになる。


まとめとして

今回は Vue.js の data プロパティの注意すべきポイントについて理解を深めてみた。なかなか仕様を理解せずに使うと罠にはまるのでこういった注意点は 1 つ 1 つ抑えていく事が重要に思える。

__________________________________

執筆者プロフィール:Katayama Yuta
SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、DevOpsの一環でCICD導入支援をする案件にも従事。 最近開発部門へ異動し、再び開発エンジニアに。座学で読み物を読むより、色々手を動かして試したり学んだりするのが好きなタイプ。

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/