最近フロントエンドの道をゆっくり進んでいて、Vue を理解した後に Typescript の学習を始めました。新しいことを学ぶたびに、自分が興味のあるものを新しい技術で作りたくなり、こうするのがとても Coooooooooooool だと思っています!👊
以前偶然に吉珠の地図を見かけて、とても優れていると思いました。偶然にも私たちの学校には関連する製品がなく、ただ描かれた画像の地図しかありませんでした。少し考えた結果、
- 携帯性を考慮して、モバイル端末で使用することに決めました。
- 学校の施設の場所を示すことができるようにします。
- 校バスのルートや乗降地点を表示します。
- 製品の紹介と学校の紹介を含めます。
- できれば学校の実景が見えるようにします。
以前に Vue を学んだこともあり、Vue を使って書くことに決めました。やると決めたらすぐに行動!✈️
そして、初日はプロジェクトの作成でつまずきました。
最初にインストールした Vue のバージョンは 2.x で、新しいプロジェクトの webpack バージョンは 3.6 でした。Typescript を使用する際に webpack を 4.x バージョンにアップグレードする必要があると表示されました。最初はあまり考えず、表示された問題を Google で検索して解決方法を探しました(検索エンジンに頼るプログラミング)。ざっと見たところ、古い依存関係をアンインストールして新しい依存関係をインストールし、設定を変更するという内容でしたが、彼らのチュートリアルは基本的に異なり、唯一共通していたのはどれも複雑で、どう選べばいいのかわからず、目に留まったものを選んで書き始めた結果、失敗しました…… その後、Vue3.x バージョンからはすでに Typescript に対応していることがわかり、プロジェクト作成時に Typescript を使用するかどうかを選択でき、自動的に設定されることがわかりました…… ああ、これ。
古いバージョンの Vue をアンインストール!
npm uninstall vue-cli -g
最新バージョンの Vue をインストール!
npm install -g @vue/cli
もう一度やってみる、出発!✈️
環境依存#
- vue/cli 4.4.6
- typescript
- element-ui:UI コンポーネント
- vue-class-component:クラスデコレーター
- vue-property-decorator:vue-class-component を基にした拡張
- vue2-svg-icon:SVG アイコンコンポーネント
- 地図リソースは高德地図 API から取得
注:vue-cli 4 バージョンは vue.config.js を自動的に作成しないため、ルートディレクトリに自分で作成して設定する必要があります。さもなければ、プロジェクトをパッケージ化する際に静的リソースが見つかりません。
Vue と Typescript の使用#
前提#
script タグに : lang=“ts” を追加
<script lang="ts">
···
</script>
コンポーネントの作成#
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class Test extends Vue {
}
コンポーネントのインポート#
import Mapmenu from '@/components/Mapmenu.vue'
@Component({
components: {
Mapmenu
}
})
data オブジェクト#
boolean や string などの単純なタイプは typescript が自動的に認識するため、型を指定する必要はありません。そうしないと、実行時にエラーが出ることがあります……
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
export default class Map extends Vue {
map: any = null
dialog = false
name = "MAGREN"
}
method メソッド#
カンマで区切る必要はなく、export default の中に直接書きます。戻り値の型を明記する必要があります。
<script lang="ts">
import { Component, Vue, Prop, Emit } from 'vue-property-decorator';
import markers from '@/config/markers.ts'
@Component
export default class Mapmenu extends Vue {
show = true
// methods
//学校紹介
toPageSchoolInfo(): void {
this.$router.push({
path: '/schoolinfo'
})
}
………………
}
Prop および Emit を使用した親子コンポーネント間の引数の受け渡し#
親コンポーネント
= 'map’は親コンポーネントの map 引数を子コンポーネントに渡し、map という名前を付けます。
@show-dialog=“handleChildValue” は子コンポーネントから渡された値を handleChildValue メソッドで処理します。
<template>
<Mapmenu :map='map' @show-dialog="handleChildValue"></Mapmenu>
</template>
<script lang="ts">
// @は/srcへのエイリアスです
import AMap from '@/config/amap.ts'
import Mapmenu from '@/components/Mapmenu.vue'
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: {
Mapmenu
}
})
export default class Map extends Vue {
dialog = false
……
private handleChildValue(val: boolean) {
// val: 子コンポーネントから渡された値
this.dialog = val;
}
}
子コンポーネント
親コンポーネントから渡された引数は this.xxx で直接使用します。
親コンポーネントに引数を渡すには、@Emit デコレーター内のメソッドを呼び出します。
<script lang="ts">
//PropとEmitをインポート
import { Component, Vue, Prop, Emit } from 'vue-property-decorator';
import markers from '@/config/markers.ts'
@Component
export default class Mapmenu extends Vue {
show = true
//親コンポーネントから渡された値を受け取る
@Prop()
private map: any
//親コンポーネントに値を渡す
@Emit()
private showDialog() {
return this.show
}
}
高德地図の読み込みと使用#
高德地図の読み込み#
地図をラップし、Promise を通じて非同期で読み込みます。TypeScript はコンパイル時に window の型を判断し、直接 window.xx を呼び出すことを許可しません。any 型に変更すれば使用できます。読み込む前に既に存在するかどうかを確認します。
export default function MapLoader(): Promise<void> {
return new Promise((resolve, reject) => {
const win: any = window
if (win.AMap) {
resolve(win.AMap)
} else {
const url = '高德地図API'
const script: HTMLScriptElement = document.createElement('script')
script.charset = 'utf-8'
script.src = url
script.onerror = reject
document.head.appendChild(script)
}
win.onLoad = () => {
resolve(win.AMap)
}
})
}
必要な場所で呼び出します。
<template>
<div id="map">
<div id="container">
</div>
</div>
</template>
<script lang="ts">
import AMap from '@/config/amap.ts'
import { Component, Vue } from 'vue-property-decorator';
export default class Map extends Vue {
map: any = null
//地図を初期化
async initAMap(): Promise<void> {
try {
const res: any = await AMap();
this.map = new res.Map("container", { //idがcontainerのdivに装載
viewMode: '3D', // 地図モード、携帯では2D効果のみ
resizeEnable: true, //地図コンテナのサイズ変化を監視するか
zoom: 17, //地図の初期レベル
center: [113.172847, 23.43399], //地図の初期中心点
pitch: 40, // 地図の俯瞰角度、有効範囲 0度-83度
buildingAnimation: true, //3D地図表示アニメーション
});
this.personOptions(this.map, true)
} catch (err) {
console.error(err);
}
}
mounted() {
this.initAMap();
}
}
</script>
地図にマーカーを追加#
const win: any = window
const marker = new win.AMap.Marker({
position: new win.AMap.LngLat(113.171688, 23.433279), //マーカーの座標
})
マーカーの上にテキストを追加:
marker.setLabel({ //labelはデフォルトで青い枠に白い背景で左上に表示される
offset: new win.AMap.Pixel(0, -3), //テキストラベルのオフセットを設定
content: "商業街", //テキストラベルの内容を設定
direction: 'top' //テキストラベルの方向を設定
});
スタイルを変更:
この時、css タグに scoped を付けてはいけません。そうしないと効果がありません。この問題は css のスコープに関わります。
.amap-marker-label{
padding: 5px;
border-radius: 3px;
border-color: ##54B7E7;
border-width: 0px;
color: ##54B7E7;
}
最後に add メソッドを呼び出して追加します:
this.map.add(marker)