こしあん
2022-03-12

Vue3でVueのインスタンスの外側からデータや関数を参照できない場合


4.8k{icon} {views}


すべてのコードがVueで完結しない場合など、Vueのインスタンスの外側からVueの関数やデータを参照したいことがたまにあります。そこでしょうもないハマり方したのでメモがてらに書いていきます。

先に正しいコード

おそらくこれはアンチパターンだと思いますが、例えばすべてのコードがVueで完結しない場合など、Vueのインスタンスの外側からVueの関数やデータを参照したいことがあります。

例えば次のような例です。これは正しいコードです。

<html>
    <head>
        <script src="https://unpkg.com/vue@next"></script>
    </head>
    <body>
        <div id="app">
            {{count}} 回クリックされました
            <button id="inner-app-btn" v-on:click="innerAppButtonOnClicked">アプリの内側のボタン</button>
        </div>
        <div>
            <button id="outer-app-btn", onclick="outerAppButtonOnClicked()">アプリの外側のボタン</button>
        </div>

        <script>
            const vueApp = Vue.createApp({
                data(){
                    return {
                        count: 0
                    };
                },
                methods: {
                    innerAppButtonOnClicked(){
                        this.count++;
                    }
                }
            }).mount("#app")

            // Vueの外側からイベントを叩く(どうしても使わないといけない場合)
            function outerAppButtonOnClicked(){
                vueApp.innerAppButtonOnClicked();
            }
        </script>
    </body>
</html>

「アプリの内側のボタン」は、Vueの内側で定義したボタンで、これが本来の書き方です。「アプリの外側のボタン」は、どうしてもVueインスタンスの内部のオブジェクトに対し、例えばピュアスクリプトでアクセスしないといけないケースです。あんまり勧められた書き方ではありませんが、どうしてもこうしないといけないケースはあります。

結論からいうと、内側と外側のボタンいずれも押すとカウンターが増えます。外側のボタンを押しても別途View側を更新しないといけない(リアクティブではなくなる)ということはないようです。

何が起きたか

これができるということは前から知っていたので、それで書いていたら次のようなエラーが出てしまいました(上のは単純化したコードで、実際はもっと複雑なコードでした)。

発生した事象は、内側のボタンは反応するが、外側のボタンをクリックすると以下のようなエラーがコンソールに表示され、更新が反映されません。

index.html:31 Uncaught TypeError: vueApp.innerAppButtonOnClicked is not a function
at outerAppButtonOnClicked (index.html:31:24)
at HTMLButtonElement.onclick (index.html:11:77)

自分の記憶違いで「あれ、Vue3でグローバル的に扱うときの書き方変わったっけ?」とグローバル変数のところを見ていたら埒が明かず。結局ここだけで3時間ぐらい溶かしたという初心者特有の苦い思い出があります。

なぜこうなってしまったのか

原因、Vue.createAppとvueApp.mount(“#app”)の返り値が異なるからです。間違った例ではこのように書いてしまいました。

        <script>
            const vueApp = Vue.createApp({
                data(){
                    return {
                        count: 0
                    };
                },
                methods: {
                    innerAppButtonOnClicked(){
                        this.count++;
                    }
                }
            });
            vueApp.mount("#app"); // ここがダメ

            // これはエラー
            function outerAppButtonOnClicked(){
                vueApp.innerAppButtonOnClicked();
            }
        </script>

Vue.createAppの結果が上で、.mount(#app)の結果が下です。上のほうが関数をグローバルに参照できそうに見えますが、実際にcountやinnerAppButtonClickedが直接参照できる形で登録されているのは下でした。要するに、「vueAppを.mount(#app)の結果でなかった」のが問題でした。

結局巡り巡って下の記事見て気づいたという。あまりに単純なミスだったんで、僕の3時間返して!(切実)

参考:https://stackoverflow.com/questions/64758293/how-to-call-a-custom-vue-3-method-from-outside-vue-app-in-javascript



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Tags:,
2 Comments

Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です