Vue3.0 + TypeScript + Mapbox GL JSの環境構築

## はじめに

Vue.jsが好きです（告白）

最近は寝ても醒めてもVueの事ばかり考えてしまって、食事はごはんとおかずとお味噌汁くらいしか喉を通らず、夜も8時間くらいしか眠れません

そんな注目の的のVue、今年v3.0が正式リリースされ、そろそろ勉強しておこうと試した訳ですが、マップライブラリ周りの情報が少ないような気がしたので、本記事ではVue3.0 + TypeScript + Mapbox GL JSの環境構築方法をまとめます

<b>Vue.js最高！</b>

## もくじ

<!-- TOC -->

- [Vue3.0プロジェクトの生成](#vue30プロジェクトの生成)
- [Mapbox GL JSの環境構築](#mapbox-gl-jsの環境構築)
    - [インストール](#インストール)
- [地図コンポーネントの作成](#地図コンポーネントの作成)
    - [デフォルト](#デフォルト)
    - [Mapbox GL JS導入](#mapbox-gl-js導入)
- [VSCodeの設定](#vscodeの設定)
    - [マウスホバーとコード補完](#マウスホバーとコード補完)
    - [コードフォーマット](#コードフォーマット)

<!-- /TOC -->

## Vue3.0プロジェクトの生成

まずCLIツールを更新しておきましょう

```
npm install -g @vue/cli
```

お好みのディレクトリで以下のコマンドで、Vueアプリケーションを対話形式で生成出来ます

```
vue create newapp
```

Vue3.0+TypeScript環境をつくるために、以下のとおり入力していきます

```
Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: 
 ◉ Choose Vue version
 ◉ Babel
❯◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfill
s, transpiling JSX)? Yes
? Pick a linter / formatter config: Basic? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> 
to invert selection)
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated confi
g files
? Save this as a preset for future projects? No
```

すると、上記の例では「newapp」というディレクトリが生成され、プロジェクトファイル一式が出力されます

以下のコマンドで

```
cd newapp
npm install
npm run serve
```

いつものウェルカムページが表示されます

![スクリーンショット 2020-12-13 8.29.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/3d81f98d-ae4a-cdfe-4e14-cbf76adcb36b.png)


## Mapbox GL JSの環境構築

### インストール

いつものように``npm install mapbox-gl``とすると、
一悶着あった
最新のv2.0がインストールされ、Mapbox APIトークンが必要になります（Mapboxのデータを使わなくても、マップの読み込みの都度APIアクセスが発生します）。

ということで、とりあえず従来と同じように使いたかったら

```
npm install mapbox-gl@v1.13.0
```

と、OSS時代の最終バージョンを指定しましょう  
型定義も忘れずに…

```
npm install @types/mapbox-gl
```

## 地図コンポーネントの作成

### デフォルト

```vue:HelloWorld.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: String,
  },
});
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

```
</details>

### Mapbox GL JS導入

```vue:HelloWorld.vue
<template>
    <div>
        {{ msg }}
        <div id="map" />
    </div>
</template>

<script lang="ts">
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

import { defineComponent, onMounted, reactive } from 'vue';
export default defineComponent({
    name: 'HelloWorld',
    props: {
        msg: String,
    },
    setup(props, context) {
        const mapstyle = reactive<mapboxgl.Style>({
            version: 8,
            sources: {
                OSM: {
                    type: 'raster',
                    tiles: ['http://tile.openstreetmap.org/{z}/{x}/{y}.png'],
                    tileSize: 256,
                    attribution:
                        '<a href="http://osm.org/copyright">© OpenStreetMap contributors</a>',
                },
            },
            layers: [
                {
                    id: 'OSM',
                    type: 'raster',
                    source: 'OSM',
                    minzoom: 0,
                    maxzoom: 18,
                },
            ],
        });
        onMounted(() => {
            const map = new mapboxgl.Map({
                container: 'map',
                style: mapstyle,
                center: [140.0, 38.2],
                zoom: 9,
                maxZoom: 18,
            });
        });
    },
});
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
    height: 500px;
}
</style>

```

するとこうなる

![スクリーンショット 2020-12-13 8.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/021d4677-8564-ec49-1cb1-decb4189c7a4.png)

ここで、見慣れないsetup()という関数以下が、CompositionAPIなるものです。これまでVueを使っていたらすぐに使えそうな感触でした（ほんとはこの辺もまとめたかったのですが時間的なアレで割愛）。


## VSCodeの設定

これで動くっちゃ動くんですが、もしかしたらマウスホバーしても変数定義は見られないし、コード補完も効いてないんじゃないでしょうか。TypeScriptの魅力の4割くらいはそこにあると思っているので、このままではいけません。

### マウスホバーとコード補完

エクステンション「Vetur」が必要です。

![スクリーンショット 2020-12-13 8.47.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/a1134547-3bc7-c96a-2839-7c003b39a4c2.png)

https://marketplace.visualstudio.com/items?itemName=octref.vetur

### コードフォーマット

で、私の環境だとコード補完だとかはこれで解決したんですが、コードフォーマットが効かなくなりました。Veturのインストール直後からです。これは、VSCodeのフォーマットは「Prettier」に任せているからでした。Veturにもフォーマット機能が内蔵されていて競合するっぽいので、エクステンションの設定から

![スクリーンショット 2020-12-13 8.50.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/ad4a90b0-2eba-0e96-1503-3f6b4f5c855d.png)

``Vetur > Format: Enable``という項目のチェックを外したら解決しました。

## おわりに

まだ実際にこの構成でアプリケーションを作ってはいませんが、ただでさえ最高なVue.jsにTypeScriptが加わる事でアホほど快適な開発環境になるのではないでしょうか。Mapbox GL JSはVue.jsとの相性も非常によく（個人の感想です）、さらにTypeScriptによる強力なコード補完で向かうところ敵なしです。

さぁみなさんもご一緒に

<b>Vue.js最高！</b>