Vue.jsでGoogle Mapを利用する場合にvue2-google-mapsといったライブラリががありますが本文書でライブラリを利用せずにVue.jsでGoogle Mapを利用する方法を解説しています。Vue CLIでVueプロジェクトの作成経験がある人を対象に記述しているのでVueを起動するまでの詳しい説明は行っていません。

Google Mapsライブラリのscriptタグをindex.htmlに追加する方法とscriptタグをJavaScriptを利用して動的に追加する方法で動作確認を行っています。動作確認する中で発生した問題の対応方法についても記述しています。

Googleアカウントの作成

Google Mapを利用するためにはGoogleアカウントが必要となります。普段使っているアカウントを利用したくない人は新規で作成を行ってください。

Googleアカウントを取得するためには本人確認のために電話番号が必須となります。

Google Cloud Platformの登録

Googleアカウント取得後はGoogle Mapを利用するために必要となるAPIキーを取得するためにGoogle Maps Platformにアクセスします。

Google Maps Platform
Google Maps Platform

右上の”始める”ボタンをクリックするとGoogle Cloud Platformの無料トライアルの登録画面が表示されるので必要な情報を入力する必要があります。利用規約の確認と個人情報、クレジットカード情報の入力が必要となります。

ステップ1では利用規約の確認を行います。

入力フォームStep1
入力フォームStep1

”続行”ボタンをクリックするとアカウントの種類(ビジネスor個人)や名前と住所、クレジット情報を入力する必要があります。

Step2の個人情報入力画面
Step2の個人情報入力画面

上記のページの説明にある通りGoogle Platformの無料トライアルの登録なので90日間で300ドルのクレジットが無料で使えます。無料トライアル期間が終了しても自動的には請求されることはありません。

無料トライアルの設定が完了するとようこそ画面が表示されるので”OK”を押してください。

登録完了画面
登録完了画面

APIの有効化するためにマップ、ルート、プレイスから使いたいAPIを選択します。今回はGoogle Mapを利用して場所の指定を行うのでプレイスを選択します。

APIの有効化を行う画面
APIの有効化を行う画面

プレイスをチェックしたら”有効にする”ボタンをクリックしてください。

プレイスを有効にする
プレイスを有効にする

有効にするとAPIキーが表示されるのでコピーして”完了”ボタンを押してください。

APIの取得
APIの取得

Google Maps Platformのダッシュボードが表示されます。各APIのリクエスト回数や請求額が表示されます。

Google mapのダッシュボード
Google mapのダッシュボード

Vue.jsの環境構築

本書ではVue.jsはVue CLIを使って作成します。


 % vue create vue-google-map 

Default Vue 2を選択します。


? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 2] babel, eslint) 
  Default (Vue 3 Preview) ([Vue 3] babel, eslint) 
  Manually select features 

インストールが完了したら、componentsフォルダにGoogleMap.vueファイルを作成します。

GoogleMap.vueファイルにはh1タグを記述します。


<template>
  <h1>Google Map</h1>
</template>

App.vueファイルを開いて、Hello Worldコンポーネントを作成したGoogleMapコンポーネントに変更します。


<template>
  <div>
    <google-map />
  </div>
</template>

<script>
import GoogleMap from './components/GoogleMap.vue'

export default {
  name: 'App',
  components: {
    GoogleMap
  }
}
</script>

プロジェクトフォルダでnpm run devコマンドを実行して下記の画面が表示されることを確認します。

Vue.jsの動作確認
Vue.jsの動作確認

Vue.jsの環境構築は完了です。

スクリプトタグを利用した表示方法

publicフォルダにあるindex.htmlにscriptタグを追加してGoogle Mapの表示を行います。Google Mapの公式ドキュメントを参考にしながら行っています。

ドキュメントに記載されているscriptタグは下記となります。本文書ではkeyの設定値にはYOUR_API_KEYと記述していきますが、これはGoogle Maps Platformで取得したAPIキーを設定してください。


<script async
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

callback=initMapが設定されているため、Google Mapのロードが完了するとinitMap関数が実行されますが定義していないためエラーが発生します。のちほどinitMap関数を利用しますがここでは削除しています。


<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY">
    </script>
  </body>
</html>
scriptタグはheadタグの閉じタグの前に追加することもできます。

ブラウザで確認すると何も変化はありませんがデベロッパーツールのコンソールでgoogleと打つとscriptタグによって作成されたgoogleオブジェクトを確認することができます。index.htmlにscriptタグを追加することでGoogle Mapが動作していることが確認できます。

コンソールでgoogleを確認
コンソールでgoogleを確認

Vue.js内でのgoogleオブジェクトの確認

Vue.jsの中でGoogle Mapを扱うためライフサイクルフックmountedの中でgoogleオブジェクトを確認します。


<template>
  <h1>Google Map</h1>
</template>

<script>
export default {
  mounted(){
    console.log(window.google)
  }
}
</script>

デベロッパーツールで確認するとGoogle Mapのロードが完了していない時にはundefinedと表示され、ロードが完了している場合はmapsが表示されます。

ロードの完了を確認する処理を追加しておきます。setIntervalを利用して500ms毎にwindow.googleの値をチェック(値があればロード完了と判断)しています。内部でclearIntervalを設定してsetIntervalの処理を解除しています。もしclearIntervalを設定しない場合はロード完了後も500ms毎にコンソールにwindow.googleの値が表示されます。


<template>
  <h1>Google Map</h1>
</template>

<script>
export default {
  mounted(){
    console.log(window.google)
    let timer = setInterval(() => {
      if(window.google){
        clearInterval(timer);
        console.log(window.google)
      }
    },500)
  }
}
</script>

setIntervalではなくsetTimeOutを利用してもロードの確認を行えます。setTimeOutの場合は1度だけ指定時間後に処理が実行されのでロードが遅い場合はエラーメッセージを表示させるようにしています。


<template>
  <h1>Google Map</h1>
</template>

<script>
export default {
  mounted(){
    console.log(window.google)
    setTimeout(() => {
      if(!window.google){
        console.log('ロードに時間がかかっています。')
      }else{
        console.log(window.google)
      }
    },3000)
  }
}
</script>

Googleマップの表示

Googleマップを表示するためのdivを追加します。ref属性をつけることによりVue.jsのrefs機能を使ってVue.jsからこの要素に直接アクセスを行います。


<template>
<div>
  <h1>Google Map</h1>
  <div ref="map"></div>
</div>
</template>
refではなくidを利用してdocument.getElementByIdで要素を取得することも可能です。

Google Mapのドキュメントに記載されているものと同じ位置を指定しているためオーストラリアのシドニー付近の地図が表示されます。


<template>
<div>
 <h1>Google Map</h1>
 <div ref="map" style="height:500px;width:800px;"></div>
</div>
</template>

<script>
export default {
  data(){
    return {
      map:'',
    }
  },
  mounted(){
    let timer = setInterval(() => {
      if(window.google){
        clearInterval(timer);
        this.map = new window.google.maps.Map(this.$refs.map, {
          center: {lat: -34.397, lng: 150.644},
          zoom: 8
        });       
      }
    },500)
  }
}
</script>
GoogleMapがブラウザ上に表示
GoogleMapがブラウザ上に表示

Googleマップにマーカーを表示

マップで指定した位置にマーカーを表示します。位置はデータプロパティのmyLatLngに保存しています。window.google.maps.Markerの引数のオブジェクトに位置と表示するmapを指定しています。


<template>
<div>
  <h1>Google Map</h1>
  <div ref="map" style="height:500px;width:800px;"></div>
</div>
</template>

<script>
export default {
  data(){
    return {
      map:'',
      myLatLng:{lat: -34.397, lng: 150.644},
    }
  },
  mounted(){
    let timer = setInterval(() => {
      if(window.google){
        clearInterval(timer);
        this.map = new window.google.maps.Map(this.$refs.map, {
          center: this.myLatLng,
          zoom: 8
        });
        new window.google.maps.Marker({position:this.myLatLng,map:this.map})
      }
    },500)
  }
}
</script>

ブラウザで確認すると地図上にマーカーを確認することができます。

マップ上にMakerを表示
マップ上にMakerを表示

scriptタグを動的に作成する方法

先ほどの設定ではscriptタグをindex.htmlに貼り付けていましたが、index.htmlに貼り付けるということはGoogleMapが利用されていないページにアクセスしてもGoogle Mapのライブラリをダウンロードすることになります。マップを表示するコンポーネントのみGoogle Mapのライブラリをダウンロードできるようにページに動的にscriptタブを追加する方法を確認します。

動的に追加する方法もGoogleのドキュメントに記載されているのでそのコードを参考にしています。

先ほどindex.htmlに追加したGoogle mapのscriptタグを削除してから行います。

document.createElementでscriptタグの要素を作成し、document.head.appendChildで作成したscriptタグをheadタグの中に追加してします。残りのコードの変更はありません。


<template>
<div>
  <h1>Google Map</h1>
  <div ref="map" style="height:500px;width:800px;"></div>
</div>
</template>

<script>
export default {
  data(){
    return {
      map:'',
      myLatLng:{lat: -34.397, lng: 150.644},
    }
  },
  mounted(){
    let script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY';
    script.async = true;
    document.head.appendChild(script);

    let timer = setInterval(() => {
      if(window.google){
        clearInterval(timer);
        this.map = new window.google.maps.Map(this.$refs.map, {
          center: this.myLatLng,
          zoom: 8
        });
        new window.google.maps.Marker({position:this.myLatLng,map:this.map})
      }
    },500)
  }
}
</script>

ブラウザをリロードすると先ほどと同様にブラウザ上にマップが表示されていることを確認することができます。

動的にscriptタグを追加してマップ表示
動的にscriptタグを追加してマップ表示

callbackのinitMapを利用する方法

scriptタグの後ろについていたcallback=initMapを利用してマップを表示することができるか確認を行います。

initMapを実行するためにコードの中では、windowをつけてwindow.initMapとする必要があります。Google Mapのライブラリのロードが完了してからinitMapは実行されるのでsetIntervalを使っていません。


mounted(){
  let script = document.createElement('script');
  script.src = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap';
  script.async = true;
  document.head.appendChild(script);

  window.initMap = () => {
    this.map = new window.google.maps.Map(this.$refs.map, {
      center: this.myLatLng,
      zoom: 8
    });
    new window.google.maps.Marker({position:this.myLatLng,map:this.map})
  }
}

複数のコンポーネントを同時に表示

scriptタグを動的に作成する方法ではコンポーネント毎にscriptタグを作成してためコンソールに下記のメッセージが表示されます。1つのページに複数回Google Maps JavaScript APIが含まれているというエラーです。ブラウザ上にはマップは表示されます。

You have included the Google Maps JavaScript API multiple times on this page. This may cause unexpected errors
Vue Routerを利用している場合に移動先のページでもGoogle Mapコンポーネントを利用している場合はページを移動する度にscriptタグが追加されていくので同様のエラーメッセージが表示されます。

デベロッパーツールからElementsを見ると2つのGoogleMapコンポーネントを1つのページに2つ使っている場合は、headタグの中に2つのscriptタグが挿入されていることが確認できます。

index.htmlにscriptタグを追加する場合はこの問題は発生しません。

1つのページ複数のscriptタグ(Google Map)が追加される問題を解決するためには複数回scriptタグが登録されない仕組みを組み込む必要があります。

下記に問題に対応したコードを記述していますが、複数のscriptタグを追加しないようにwindowオブジェクトにmapLoadStartedという変数を設定して1つのコンポーネントのみでscriptタグ追加のコードを実行するように制御しています。mapLoadStartedはGoogle Mapライブラリのロードを開始したよという意味です。


mounted(){

  if(!window.mapLoadStarted){
    window.mapLoadStarted = true;
    let script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap';
    script.async = true;
    document.head.appendChild(script);
  }

  window.initMap = () => {
    window.mapLoaded = true;
  }

  let timer = setInterval(() => {
    if(window.mapLoaded){
      clearInterval(timer);
      this.map = new window.google.maps.Map(this.$refs.map, {
        center: this.myLatLng,
        zoom: 4
      });
      new window.google.maps.Marker({position:this.myLatLng,map:this.map})
    }
  },500)
},

またwindowオブジェクトにmapLoadedという変数を設定して、Google Mapライブラリのロードが完了したら実行されるinitMap関数を使ってmapLoadedの値をtrueにしています。trueになっている場合はロードが完了しているのでマップの描写のコードを実行しています。

windowオブジェクトにmapLoadedを加えることで”You have included the Google Maps JavaScript API multiple times on this page. This may cause unexpected errors”は解消しました。Vue Routerを利用してページを移動する場合にmapLoadedを使っていない場合はGoogleMapコンポーネントを利用しているページに移動する度にscriptタグが追加されエラーが発生しましたが設定後はエラーは解消されました。

propsを使ってマップの位置を変更

GoogleMapコンポーネントにpropsを追加し、表示するマップの場所を変更できるように更新します。またzoomで表示する地図の拡大・縮小率も変更できるようにします。

propsを使って親コンポーネントからmyLatLngとzoomの値を受け取ります。


<script>
export default {
  props:{
    myLatLng:{
      type:Object,
      required:true,
    },
    zoom:{
      type:Number,
      required:true,
    }
  },
  data(){
    return {
      map:'',
    }
  },
  mounted(){
    let script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY';
    script.async = true;
    document.head.appendChild(script);
    
    let timer = setInterval(() => {
      if(window.google){
        clearInterval(timer);
        this.map = new window.google.maps.Map(this.$refs.map, {
          center: this.myLatLng,
          zoom: this.zoom
        });
        new window.google.maps.Marker({position:this.myLatLng,map:this.map})
      }
    },500)
  }
}
</script>

GoogleMapコンポーネントをimportしている親コンポーネントのApp.vueではpropsで経度と緯度とzoomの値を渡します。


<template>
  <div>
    <google-map :myLatLng="{lat: -25.344, lng: 131.036}" :zoom="4" />
  </div>
</template>

先ほどとは異なる場所を指定したのでオーストラリアのウルルを中心としたマップが表示されます。

propsで値を渡してマップを表示
propsで値を渡してマップを表示

他にもGoogle Mapにはオプションが存在するのでここまでの基礎を理解することで使いこなすことができます。