跳至主要內容
Skip to content

呼叫原生分享選單:Web Share API 實戰

在行動網頁上,我們常常看到各種社群媒體的分享按鈕(Facebook, Line, Twitter...)。但這些按鈕往往風格不一,且無法涵蓋使用者所有安裝的 App。

Web Share API 解決了這個問題。它允許網頁像原生 App 一樣,直接呼叫作業系統的「分享選單 (Share Sheet)」。這意味著使用者可以自由選擇將內容分享到 Line、Telegram、AirDrop 或是複製到剪貼簿。


一、 Web Share API 核心

1.1 基本語法

API 掛載於 navigator.share(),它回傳一個 Promise。

javascript
if (navigator.share) {
  navigator.share({
    title: '文章標題',
    text: '這是一篇好文章',
    url: 'https://example.com',
  })
  .then(() => console.log('分享成功'))
  .catch((error) => console.log('分享失敗或取消', error));
}

1.2 關鍵限制

為了安全性與使用者體驗,此 API 有嚴格限制:

  1. HTTPS: 僅限在 HTTPS 環境下運作(localhost 除外)。
  2. 使用者互動: 必須由使用者點擊按鈕等行為觸發,不能自動執行。
  3. 支援度: 主要支援行動裝置瀏覽器(Chrome for Android, Safari on iOS)。桌機版 Chrome/Edge/Safari 近期也已支援,但行為可能不同。

1.3 檔案分享

除了文字與連結,Web Share API Level 2 還支援檔案分享:

javascript
if (navigator.canShare && navigator.canShare({ files: [file] })) {
  navigator.share({
    files: [file],
    title: '分享圖片',
    text: '看看這張照片'
  });
}

二、 封裝 useShare Composable

我們將封裝一個 useShare,讓他自動檢測支援度,並提供簡單的呼叫介面。

2.1 介面定義

typescript
export interface ShareOptions {
  title?: string;
  text?: string;
  url?: string;
  files?: File[];
}

export interface UseShareReturn {
  isSupported: boolean;
  canShare: (options: ShareOptions) => boolean;
  share: (options: ShareOptions) => Promise<void>;
}

2.2 完整實作

typescript
// composables/useShare.ts
import { ref } from 'vue';

export function useShare() {
  const isSupported = typeof navigator !== 'undefined' && 'share' in navigator;

  /**
   * 檢查是否支援分享特定的內容
   */
  const canShare = (options: ShareOptions) => {
    if (!isSupported) return false;
    
    // 如果瀏覽器支援 navigator.canShare (Level 2)
    if (typeof navigator.canShare === 'function') {
      return navigator.canShare(options);
    }
    
    // 回退機制:只要有 API 就假設可以分享純文字
    return !options.files; 
  };

  /**
   * 執行分享
   */
  const share = async (options: ShareOptions) => {
    if (!isSupported) {
      throw new Error('Web Share API is not supported');
    }

    try {
      await navigator.share(options);
      return true;
    } catch (err: any) {
      if (err.name === 'AbortError') {
        // 使用者取消分享是正常行為,不視為錯誤
        console.log('使用者取消分享');
        return false;
      }
      throw err;
    }
  };

  return {
    isSupported,
    canShare,
    share
  };
}

三、 實戰範例:分享按鈕元件

這個範例展示一個通用的分享按鈕,如果瀏覽器不支援 Web Share API,則降級為「複製連結」功能。

vue
<script setup lang="ts">
import { useShare } from '@/composables/useShare';
import { useClipboard } from '@/composables/useClipboard'; // 假設前幾章的 Composable

const props = defineProps<{
  title: string;
  url: string;
}>();

const { isSupported, share } = useShare();
const { copy, copied } = useClipboard();

const handleShare = async () => {
  const shareData = {
    title: props.title,
    text: `推薦你閱讀:${props.title}`,
    url: props.url
  };

  if (isSupported) {
    await share(shareData);
  } else {
    // 降級處理:複製連結
    await copy(props.url);
  }
};
</script>

<template>
  <button @click="handleShare" class="share-btn">
    <span v-if="isSupported">
      📲 分享文章
    </span>
    <span v-else>
      {{ copied ? '✅ 已複製' : '🔗 複製連結' }}
    </span>
  </button>
</template>

<style scoped>
.share-btn {
  background: #f0f0f0;
  border: 1px solid #ccc;
  padding: 8px 16px;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.2s;
}
.share-btn:hover {
  background: #e0e0e0;
}
</style>

四、 測試與除錯

由於 localhost (除了 127.0.0.1) 以外的 HTTP 網域無法使用此 API,建議使用以下方式測試:

  1. 本機測試:直接使用 http://localhost:3000
  2. 手機測試:使用 ngrok 或類似工具將 localhost 轉發為 HTTPS URL,再用手機開啟。

總結

Web Share API 是提升行動網頁體驗的關鍵拼圖。它讓我們不再需要維護龐大的第三方 SDK,就能提供最原生、最流暢的分享體驗。記得始終提供一個「降級方案」(如複製連結),以確保所有使用者都能分享你的內容。


延伸閱讀