掌握使用者位置:Geolocation API 深度解析
在開發地圖應用、外送服務或即時天氣查詢時,獲取使用者的地理位置是不可或缺的功能。瀏覽器原生的 Geolocation API 提供了標準化的介面,讓我們能安全且精確地取得裝置的經緯度資訊。
本篇將深入探討如何使用 Geolocation API,並將其封裝成一個強大的 Vue 3 Composable,支援單次定位與即時追蹤。
一、 Geolocation API 核心基礎
1.1 隱私與權限
這是一個高度隱私敏感的 API。
- HTTPS 限制:現代瀏覽器強制要求網站必須使用 HTTPS 協定(localhost 除外)才能呼叫此 API。
- 使用者同意:瀏覽器會自動彈出視窗詢問使用者是否允許分享位置。如果被拒絕,將無法再次自動彈出,需使用者手動至設定開啟。
1.2 核心方法
API 掛載於 navigator.geolocation 物件下:
- getCurrentPosition(success, error, options):獲取「目前」的單次位置。
- watchPosition(success, error, options):監聽位置變化,當裝置移動時持續觸發。回傳一個
watchId。 - clearWatch(watchId):停止監聽。
1.3 設定選項 (PositionOptions)
javascript
const options = {
enableHighAccuracy: true, // 是否要求高精確度(會較耗電)
timeout: 5000, // 等待最長毫秒數 (預設 Infinity)
maximumAge: 0 // 接受的快取位置時間 (0 代表強制作新定位)
};二、 封裝 useGeolocation Composable
我們將實作一個 useGeolocation,讓它具備響應式的座標狀態,並提供 resume (開始追蹤) 與 pause (停止追蹤) 的控制能力。
2.1 介面定義
typescript
export interface UseGeolocationReturn {
coords: Ref<{ latitude: number; longitude: number } | null>;
locatedAt: Ref<number | null>; // 最後更新時間
error: Ref<GeolocationPositionError | null>;
isSupported: boolean;
resume: () => void;
pause: () => void;
}2.2 完整實作
typescript
// composables/useGeolocation.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useGeolocation(options: PositionOptions = {}) {
const isSupported = typeof navigator !== 'undefined' && 'geolocation' in navigator;
const coords = ref<{ latitude: number; longitude: number } | null>(null);
const locatedAt = ref<number | null>(null);
const error = ref<GeolocationPositionError | null>(null);
let watcherId: number | null = null;
const updatePosition = (position: GeolocationPosition) => {
coords.value = {
latitude: position.coords.latitude,
longitude: position.coords.longitude
};
locatedAt.value = position.timestamp;
error.value = null;
};
const onError = (err: GeolocationPositionError) => {
error.value = err;
};
const resume = () => {
if (!isSupported) return;
if (watcherId) return; // 已經在監聽中
watcherId = navigator.geolocation.watchPosition(
updatePosition,
onError,
options
);
};
const pause = () => {
if (watcherId !== null) {
navigator.geolocation.clearWatch(watcherId);
watcherId = null;
}
};
onMounted(() => resume());
onUnmounted(() => pause());
return {
isSupported,
coords,
locatedAt,
error,
resume,
pause
};
}三、 實戰範例:即時座標儀表板
這個範例展示如何顯示使用者的即時經緯度,並提供一個 Google Maps 的連結。
vue
<script setup lang="ts">
import { computed } from 'vue';
import { useGeolocation } from '@/composables/useGeolocation';
const { coords, error, locatedAt, pause, resume } = useGeolocation({
enableHighAccuracy: true
});
const mapLink = computed(() => {
if (!coords.value) return '#';
return `https://www.google.com/maps?q=${coords.value.latitude},${coords.value.longitude}`;
});
</script>
<template>
<div class="geo-panel">
<h3>📍 即時位置追蹤</h3>
<div v-if="error" class="error-msg">
定位失敗: {{ error.message }}
(Code: {{ error.code }})
</div>
<div v-else-if="coords" class="coords-info">
<div class="stat">
<label>緯度 (Lat)</label>
<span>{{ coords.latitude.toFixed(6) }}</span>
</div>
<div class="stat">
<label>經度 (Lng)</label>
<span>{{ coords.longitude.toFixed(6) }}</span>
</div>
<div class="time">
更新時間: {{ new Date(locatedAt!).toLocaleTimeString() }}
</div>
<a :href="mapLink" target="_blank" class="map-btn">
在 Google Maps 開啟
</a>
</div>
<div v-else class="loading">
正在獲取位置...
</div>
<div class="controls">
<button @click="pause">暫停追蹤</button>
<button @click="resume">繼續追蹤</button>
</div>
</div>
</template>
<style scoped>
.geo-panel {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
max-width: 400px;
}
.stat {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-family: monospace;
font-size: 1.1em;
}
.error-msg {
color: #dc3545;
background: #ffe6e6;
padding: 10px;
border-radius: 4px;
}
.map-btn {
display: block;
text-align: center;
background: #4285f4;
color: white;
padding: 8px;
border-radius: 4px;
text-decoration: none;
margin-top: 15px;
}
</style>四、 常見錯誤代碼對照表
當 onError 被觸發時,error.code 會告訴我們失敗的原因:
| Code | 常數 | 說明 |
|---|---|---|
| 1 | PERMISSION_DENIED | 使用者拒絕提供位置權限。 |
| 2 | POSITION_UNAVAILABLE | 無法獲取位置(例如 GPS 訊號不良)。 |
| 3 | TIMEOUT | 在指定時間內無法獲取位置。 |
總結
Geolocation API 雖然簡單,但細節在於「權限處理」與「錯誤回饋」。透過 watchPosition 搭配 Vue 的生命週期管理,我們能輕鬆打造出對電池友善且使用者體驗良好的定位功能。