mcpField
C
capacitor-vue

capacitor-vue

github.com/capawesome-team开发52874 次安装1 周前更新
代码仓库

关于 capacitor-vue

为使用 Vue 3 和 Capacitor 的开发者提供按项目框架调整的开发指导,涵盖组合式 API、插件、生命周期和路由等 Vue 模式。

主要功能

  1. 01检测 Vue、Capacitor 版本及框架
  2. 02组合式函数封装 Capacitor 插件
  3. 03管理插件监听器生命周期
  4. 04集成 Vue Router 深度链接
  5. 05处理 Android 返回键与平台检测

适用场景

  1. 01将 Vue 项目接入 Capacitor
  2. 02为插件创建可复用的组合式函数
  3. 03排查监听器清理与平台配置问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/capawesome-team/skills --skill capacitor-vue

安装后在 Claude Code 中输入/capacitor-vue确认已加载。

附带文件references/nuxt.mdreferences/quasar.md

详细介绍

使用 Vue 的 Capacitor

Capacitor 应用开发的 Vue 专属模式和最佳实践——Composition API、组合式函数、响应性、生命周期钩子、Vue Router 集成,以及针对 Quasar 和 Nuxt 的框架专属指南。

前提条件

  1. 使用 Vue 3 的 Capacitor 6、7 或 8 应用。
  2. 已安装 Node.js 和 npm。
  3. iOS 开发:已安装 Xcode。
  4. Android 开发:已安装 Android Studio。

Agent 行为

  • 先自动检测,再提问。 检查项目中的 vite.config.ts、vite.config.js、quasar.config.js、quasar.config.ts、nuxt.config.ts、package.json、capacitor.config.ts 或 capacitor.config.json,以及现有目录结构。仅在无法检测某项信息时询问用户。
  • 逐步引导。 一次引导用户完成一个步骤。
  • 检测元框架。 判断项目使用的是纯 Vue(Vite)、Quasar 还是 Nuxt,并相应调整说明。

操作步骤

步骤 1:分析项目

通过读取项目文件自动检测以下信息:

  1. Vue 版本:从 package.json 读取 vue 版本。
  2. Capacitor 版本:从 package.json 读取 @capacitor/core 版本。如果不存在,说明尚未添加 Capacitor——继续执行步骤 2。
  3. 元框架:按以下顺序检查文件来检测框架:
    • quasar.config.js 或 quasar.config.ts——Quasar 项目。转到 references/quasar.md。
    • nuxt.config.ts 或 nuxt.config.js——Nuxt 项目。转到 references/nuxt.md。
    • vite.config.ts 或 vite.config.js——纯 Vue(Vite) 项目。继续执行以下步骤。
  4. 平台:检查哪些目录存在(android/、ios/)。
  5. Capacitor 配置格式:检查是否存在 capacitor.config.ts(TypeScript)或 capacitor.config.json(JSON)。
  6. 构建输出目录:从 vite.config.ts 或 vite.config.js 读取 build.outDir。默认值为 dist。
步骤 2:将 Capacitor 添加到 Vue 项目

如果 package.json 中已包含 @capacitor/core,则跳过。如果项目使用 Quasar,则跳过(Quasar 有自己的 Capacitor 集成——参见 references/quasar.md)。

  1. 安装 Capacitor 核心包和 CLI:

    npm install @capacitor/core
    npm install -D @capacitor/cli
    
  2. 初始化 Capacitor:

    npx cap init
    

    出现提示时,将 web 目录设置为步骤 1 中检测到的 Vue 构建输出路径。对于基于 Vite 的 Vue 项目,该路径通常为 dist。

  3. 确认生成的 capacitor.config.ts 或 capacitor.config.json 中的 webDir 值与 Vue 构建输出路径一致。如果不一致,请更新:

    capacitor.config.ts:

    import type { CapacitorConfig } from '@capacitor/cli';
    
    const config: CapacitorConfig = {
      appId: 'com.example.app',
      appName: 'my-app',
      webDir: 'dist',
    };
    
    export default config;
    

    capacitor.config.json:

    {
      "appId": "com.example.app",
      "appName": "my-app",
      "webDir": "dist"
    }
    
  4. 构建 Vue 应用并添加平台:

    npm run build
    npm install @capacitor/android @capacitor/ios
    npx cap add android
    npx cap add ios
    npx cap sync
    
步骤 3:项目结构

基于 Vite 的 Capacitor Vue 项目具有以下结构:

my-app/
├── android/                  # Android native project
├── ios/                      # iOS native project
├── public/
├── src/
│   ├── assets/
│   ├── components/
│   ├── composables/          # Custom composables for Capacitor plugins
│   ├── router/
│   │   └── index.ts          # Vue Router configuration
│   ├── views/
│   ├── App.vue
│   └── main.ts
├── capacitor.config.ts       # or capacitor.config.json
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts

要点:

  • android/ 和 ios/ 目录包含原生项目,应提交到版本控制中。
  • src/ 目录包含 Vue 应用,即 Capacitor 应用的 Web 层。
  • 将封装 Capacitor 插件的自定义组合式函数放在 src/composables/ 中。
  • 在 src/ 中的 Vue 组件或组合式函数里调用 Capacitor 插件。
步骤 4:在 Vue 中使用 Capacitor 插件

Capacitor 插件是纯 TypeScript API。使用 Composition API 在 Vue 组件中直接导入并调用它们。

在组件中直接使用
<script setup lang="ts">
import { ref } from 'vue';
import { Geolocation } from '@capacitor/geolocation';

const latitude = ref<number | null>(null);
const longitude = ref<number | null>(null);

async function getCurrentPosition() {
  const position = await Geolocation.getCurrentPosition();
  latitude.value = position.coords.latitude;
  longitude.value = position.coords.longitude;
}
</script>

<template>
  <div>
    <p>Latitude: {{ latitude }}</p>
    <p>Longitude: {{ longitude }}</p>
    <button @click="getCurrentPosition">Get Location</button>
  </div>
</template>
将插件封装为组合式函数(推荐)

将 Capacitor 插件封装为组合式函数,可实现代码复用、封装响应式状态和自动清理:

// src/composables/useCamera.ts
import { ref } from 'vue';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import type { Photo } from '@capacitor/camera';

export function useCamera() {
  const photo = ref<Photo | null>(null);
  const error = ref<string | null>(null);

  async function takePhoto(): Promise<void> {
    try {
      error.value = null;
      photo.value = await Camera.getPhoto({
        quality: 90,
        allowEditing: false,
        resultType: CameraResultType.Uri,
        source: CameraSource.Camera,
      });
    } catch (e) {
      error.value = e instanceof Error ? e.message : String(e);
    }
  }

  async function pickFromGallery(): Promise<void> {
    try {
      error.value = null;
      photo.value = await Camera.getPhoto({
        quality: 90,
        allowEditing: false,
        resultType: CameraResultType.Uri,
        source: CameraSource.Photos,
      });
    } catch (e) {
      error.value = e instanceof Error ? e.message : String(e);
    }
  }

  return {
    photo,
    error,
    takePhoto,
    pickFromGallery,
  };
}

在组件中使用组合式函数:

<script setup lang="ts">
import { useCamera } from '@/composables/useCamera';

const { photo, error, takePhoto } = useCamera();
</script>

<template>
  <div>
    <button @click="takePhoto">Take Photo</button>
    <p v-if="error">Error: {{ error }}</p>
    <img v-if="photo?.webPath" :src="photo.webPath" alt="Captured photo" />
  </div>
</template>
步骤 5:使用生命周期钩子管理插件事件监听器

必须在 onMounted 中注册 Capacitor 插件事件监听器,并在 onUnmounted 中移除,以防止内存泄漏。Vue 的响应式系统会自动检测 ref 的变化,因此不存在与 NgZone 等效的问题——但清理仍然至关重要。

自动清理的组合式函数
// src/composables/useNetwork.ts
import { ref, onMounted, onUnmounted } from 'vue';
import { Network } from '@capacitor/network';
import type { ConnectionStatus } from '@capacitor/network';
import type { PluginListenerHandle } from '@capacitor/core';

export function useNetwork() {
  const status = ref<ConnectionStatus | null>(null);
  let listenerHandle: PluginListenerHandle | null = null;

  onMounted(async () => {
    status.value = await Network.getStatus();
    listenerHandle = await Network.addListener('networkStatusChange', (newStatus) => {
      status.value = newStatus;
    });
  });

  onUnmounted(async () => {
    await listenerHandle?.remove();
  });

  return {
    status,
  };
}

在组件中使用:

<script setup lang="ts">
import { useNetwork } from '@/composables/useNetwork';

const { status } = useNetwork();
</script>

<template>
  <p v-if="status">Network: {{ status.connected ? 'Online' : 'Offline' }}</p>
</template>
通过 App.vue 添加应用级监听器

对于应在整个应用生命周期内持续运行的监听器(例如应用状态变化),请在 App.vue 中注册:


<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { App } from '@capacitor/app';
import type { PluginListenerHandle } from '@capacitor/core';
import { RouterView } from 'vue-router';

let appStateListener: PluginListenerHandle | null = null;

onMounted(async () => {
  appStateListener = await App.addListener('appStateChange', (state) => {
    console.log('App state changed. Is active:', state.isActive);
  });
});

onUnmounted(async () => {
  await appStateListener?.remove();
});
</script>

<template>
  <RouterView />
</template>
第 6 步:平台检测

使用 Capacitor.isNativePlatform() 和 Capacitor.getPlatform() 有条件地运行仅限原生平台的代码。将其封装为可复用的组合式函数:

// src/composables/usePlatform.ts
import { Capacitor } from '@capacitor/core';

export function usePlatform() {
  const platform = Capacitor.getPlatform() as 'web' | 'ios' | 'android';
  const isNative = Capacitor.isNativePlatform();
  const isIos = platform === 'ios';
  const isAndroid = platform === 'android';
  const isWeb = platform === 'web';

  return {
    platform,
    isNative,
    isIos,
    isAndroid,
    isWeb,
  };
}

在组件中使用它来显示或隐藏仅限原生平台的功能:

<script setup lang="ts">
import { usePlatform } from '@/composables/usePlatform';

const { isNative } = usePlatform();
</script>

<template>
  <button v-if="isNative" @click="openNativeSettings()">Open Device Settings</button>
</template>
第 7 步:使用 Vue Router 处理深层链接路由

通过将 Capacitor 的 App.addListener('appUrlOpen', ...) 事件映射到 Vue Router 导航来处理深层链接。在 App.vue 或专用组合式函数中进行设置:

// src/composables/useDeepLinks.ts
import { onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { App } from '@capacitor/app';
import type { PluginListenerHandle } from '@capacitor/core';

export function useDeepLinks() {
  const router = useRouter();
  let listenerHandle: PluginListenerHandle | null = null;

  onMounted(async () => {
    listenerHandle = await App.addListener('appUrlOpen', (event) => {
      const url = new URL(event.url);
      const path = url.pathname;

      // Navigate to the route matching the deep link path.
      // Adjust the path parsing logic to match the app's URL scheme.
      if (path) {
        router.push(path);
      }
    });
  });

  onUnmounted(async () => {
    await listenerHandle?.remove();
  });
}

在 App.vue 中使用该组合式函数:


<script setup lang="ts">
import { useDeepLinks } from '@/composables/useDeepLinks';

useDeepLinks();
</script>

<template>
  <RouterView />
</template>
第 8 步:处理返回按钮(Android)

结合 Vue Router,使用 App.addListener('backButton', ...) 处理 Android 硬件返回按钮:

// src/composables/useBackButton.ts
import { onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { App } from '@capacitor/app';
import { Capacitor } from '@capacitor/core';
import type { PluginListenerHandle } from '@capacitor/core';

export function useBackButton() {
  const router = useRouter();
  let listenerHandle: PluginListenerHandle | null = null;

  onMounted(async () => {
    if (Capacitor.getPlatform() !== 'android') {
      return;
    }

    listenerHandle = await App.addListener('backButton', ({ canGoBack }) => {
      if (canGoBack) {
        router.back();
      } else {
        App.exitApp();
      }
    });
  });

  onUnmounted(async () => {
    await listenerHandle?.remove();
  });
}

在 App.vue 中使用该组合式函数:


<script setup lang="ts">
import { useBackButton } from '@/composables/useBackButton';

useBackButton();
</script>

<template>
  <RouterView />
</template>
第 9 步:设置 PWA Elements

某些 Capacitor 插件(例如 Camera、Toast)需要 @ionic/pwa-elements 才能提供 Web 回退界面。如果项目使用了这些插件中的任意一个,且目标平台包含 Web:

  1. 安装 PWA Elements:

    npm install @ionic/pwa-elements
    
  2. 在 src/main.ts 中、调用 createApp() 之前注册自定义元素:

    import { createApp } from 'vue';
    import { defineCustomElements } from '@ionic/pwa-elements/loader';
    import App from './App.vue';
    import router from './router';
    
    defineCustomElements(window);
    
    const app = createApp(App);
    app.use(router);
    app.mount('#app');
    
第 10 步:构建与同步工作流

修改 Vue 应用后,构建并同步到原生平台:

npm run build
npx cap sync

要在设备或模拟器上运行:

npx cap run android
npx cap run ios

要打开原生 IDE 进行高级配置或调试:

npx cap open android
npx cap open ios

要在开发期间启用实时重新加载:

npx cap run android --livereload --external
npx cap run ios --livereload --external

这会在内部启动 Vite 开发服务器,并配置原生应用从开发服务器加载。

错误处理

  • webDir 不匹配:如果 npx cap sync 复制了错误的文件,请确认 capacitor.config.ts 或 capacitor.config.json 中的 webDir 与 Vue 的构建输出路径一致。对于基于 Vite 的 Vue 项目,默认输出目录为 dist。
  • 运行时找不到插件:安装任何新插件后运行 npx cap sync。确认该插件已列在 package.json 依赖项中。
  • 监听器导致内存泄漏:始终在 onUnmounted 中移除插件监听器。保存 addListener 返回的 PluginListenerHandle,并在组件卸载时调用 handle.remove()。
  • 深层链接无法使用:确认已在原生项目中配置应用 URL 方案或通用链接(Android 对应 android/app/src/main/AndroidManifest.xml,iOS 对应 ios/App/App/Info.plist 和关联域名权利文件)。确认已在 App.vue 中调用 useDeepLinks()。
  • 返回按钮意外关闭应用:确保返回按钮监听器在调用 App.exitApp() 前检查 canGoBack。仅在没有导航历史记录时退出。
  • PWA Elements 未在 Web 上渲染:确认在 src/main.ts 中、调用 createApp() 之前调用了 defineCustomElements(window)。确认已安装 @ionic/pwa-elements。
  • Quasar 项目 — 未检测到 Capacitor:请使用 quasar mode add capacitor,而非手动安装 Capacitor。参见 references/quasar.md。
  • Nuxt 项目 — 构建输出不兼容:Nuxt 的默认 SSR 输出与 Capacitor 不兼容。项目必须在 nuxt.config.ts 中使用 ssr: false 来生成静态 SPA。参见 references/nuxt.md。

相关技能

  • capacitor-app-creation — 从零创建新的 Capacitor 应用。
  • capacitor-app-development — 通用的 Capacitor 开发指南,不限于 Vue。
  • capacitor-plugins — 从官方和社区来源安装并配置 Capacitor 插件。
  • ionic-vue — 使用 Vue 的 Ionic Framework(基于 Capacitor 的 UI 组件、导航和主题)。
  • capacitor-app-upgrades — 将 Capacitor 应用升级到较新的主版本。

评论

支持 Markdown