のーずいだんぷ

Occurred OutOfMemory...dumping...dumping...

Google map api をVue.js(Nuxt.js)のコンポーネントで利用する方法

やりたいこと

一般的にGoogle map api をwebアプリケーションで使おうとしたとき、例えば以下のような感じで利用する。

  • html
<!DOCTYPE html>
<html>
<head>
    <title>Simple Map</title>
    <style>
    #map {
        height: 100%;
        width: 100%;  
    } 
    </style>
    <script src="https://maps.googleapis.com/maps/api/js?key={API_KEY}&callback=initMap&libraries=places&v=weekly"></script>
    <script>
    function initMap(){
   const map = new google.maps.Map(document.getElementById("map"), {
       zoom:10,
       center: new google.map.LatLng(-34.397, 150.644)
   }
    }
    
    </script>
</head>

<body>
    <div id="map"></div>
</body>

</html>

一般的なライブラリはnpm/yarn 経由でモジュールが提供されており、その場合は特に迷うことなく使えたのだが、google map api はweb api を叩くしか方法がなさそうだった。

今回のケースのようにCDN経由でライブラリを利用する場合はVue.jsをどうやって利用するのかがわからなかったため、その実装方法について調べてみた。

結論

  • cdnライブラリを使用するときと同様にscriptタグを動的に書き込むしかない

ちなみにいくつか試してみたが、以下はだめだった

  • httpクライアントで直接getリクエストする

→ クロスドメインリクエスト(CORS)が許可されていないため、使用不可。

  • コンポーネントのtemplate要素にscriptタグを予め書き込む

→ Vue.jsではtemplate要素内にscripタグを書くことはできないため、使用不可。

また細かい話として、google map apiの場合は、リクエストパラメータにコールバック関数名を引き渡すことで、ロード後に初期化するようにしている。

具体的なコードは以下のような感じ。(Nuxt.jsを利用しているためVueインスタンスのマウント部分はない)

  • pages/index.vue
<template>
    <div id="map"></div>
</template>
<script>
import qs from query-string'

const params = {
    key: {your-APIkey},
    libraries: 'places',
    v: 'weekly',
    callback: 'handleLoadGoogleMapScript'
}

export default {
    data(){
        return google: null
    },
    mounted(){
        const mapDiv = document.getElementById('map')
        
        this.loadGoogleMapScript().then(google => {
            this.google = google
            this.initMap(mapDiv)
        })
    },
    methods{
        loadGoogleMapScript(){
            return new Promise((resolve, reject) => {
                if(window.google){
                    return resolve(window.google)
                
                }else{
                    const script = document.createElement('script')
                    script.src = `https://maps.googleapis.com/maps/api/js?${qs.stringfy(params)}`
                    const head = document.getElementsByTagName('head')[0]
                    if(!head){
                        return reject(new Error('head tag is not load'))   
                    }else{
                        head.appendChild(script)
                        window.handleLoadGoogleMapScript = () => {
                            return resolve(window.google)
                        }
                    }
                    
                    setTimeout(() => {
                        if (!window.google){
                            reject(new Error('Failed to load google map api))
                        }
                    }, 5*1000)
                }
            })
        }    
    },
    
}

</script>

参考

stackoverflow.com

qiita.com

qiita.com

stackoverflow.com

jp.vuejs.org