capacitor-vue
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCapacitor with Vue
Capacitor 结合 Vue 开发指南
Vue-specific patterns and best practices for Capacitor app development — Composition API, composables, reactivity, lifecycle hooks, Vue Router integration, and framework-specific guidance for Quasar and Nuxt.
Vue专属的Capacitor应用开发模式与最佳实践——包括Composition API、composables、响应式、生命周期钩子、Vue Router集成,以及针对Quasar和Nuxt的框架专属指导。
Prerequisites
前置条件
- Capacitor 6, 7, or 8 app with Vue 3.
- Node.js and npm installed.
- For iOS: Xcode installed.
- For Android: Android Studio installed.
- Capacitor 6、7或8 版本的Vue 3项目。
- 已安装Node.js和npm。
- 针对iOS开发:已安装Xcode。
- 针对Android开发:已安装Android Studio。
Agent Behavior
Agent 行为规范
- Auto-detect before asking. Check the project for ,
vite.config.ts,vite.config.js,quasar.config.js,quasar.config.ts,nuxt.config.ts,package.jsonorcapacitor.config.ts, and existing directory structure. Only ask the user when something cannot be detected.capacitor.config.json - Guide step-by-step. Walk the user through the process one step at a time.
- Detect the meta-framework. Determine whether the project uses plain Vue (Vite), Quasar, or Nuxt, and adapt instructions accordingly.
- 自动检测优先,按需询问:检查项目中的、
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,并据此调整指导内容。
Procedures
操作步骤
Step 1: Analyze the Project
步骤1:分析项目
Auto-detect the following by reading project files:
- Vue version: Read version from
vue.package.json - Capacitor version: Read version from
@capacitor/core. If not present, Capacitor has not been added yet — proceed to Step 2.package.json - Meta-framework: Detect the framework by checking for these files in order:
- or
quasar.config.js— Quasar project. Proceed toquasar.config.ts.references/quasar.md - or
nuxt.config.ts— Nuxt project. Proceed tonuxt.config.js.references/nuxt.md - or
vite.config.ts— Plain Vue (Vite) project. Continue with the steps below.vite.config.js
- Platforms: Check which directories exist (,
android/).ios/ - Capacitor config format: Check for (TypeScript) or
capacitor.config.ts(JSON).capacitor.config.json - Build output directory: Read from
build.outDirorvite.config.ts. The default isvite.config.js.dist
通过读取项目文件自动检测以下信息:
- Vue版本:从中读取
package.json的版本。vue - Capacitor版本:从中读取
package.json的版本。如果未找到该依赖,说明Capacitor尚未添加——请继续步骤2。@capacitor/core - 元框架:按以下顺序检查文件以识别框架:
- 存在或
quasar.config.js—— Quasar 项目,请参考quasar.config.ts。references/quasar.md - 存在或
nuxt.config.ts—— Nuxt 项目,请参考nuxt.config.js。references/nuxt.md - 存在或
vite.config.ts—— 纯Vue(Vite) 项目,请继续以下步骤。vite.config.js
- 存在
- 目标平台:检查是否存在、
android/目录。ios/ - Capacitor配置格式:检查是(TypeScript格式)还是
capacitor.config.ts(JSON格式)。capacitor.config.json - 构建输出目录:从或
vite.config.ts中读取vite.config.js的值,默认是build.outDir。dist
Step 2: Add Capacitor to a Vue Project
步骤2:为Vue项目添加Capacitor
Skip if is already in . Skip if the project uses Quasar (Quasar has its own Capacitor integration — see ).
@capacitor/corepackage.jsonreferences/quasar.md-
Install Capacitor core and CLI:bash
npm install @capacitor/core npm install -D @capacitor/cli -
Initialize Capacitor:bash
npx cap initWhen prompted, set the web directory to the Vue build output path detected in Step 1. For Vite-based Vue projects, this is typically.dist -
Verify thevalue in the generated
webDirorcapacitor.config.tsmatches the Vue build output path. If incorrect, update it:capacitor.config.json:capacitor.config.tstypescriptimport type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'my-app', webDir: 'dist', }; export default config;:capacitor.config.jsonjson{ "appId": "com.example.app", "appName": "my-app", "webDir": "dist" } -
Build the Vue app and add platforms:bash
npm run build npm install @capacitor/android @capacitor/ios npx cap add android npx cap add ios npx cap sync
如果中已存在,请跳过此步骤。如果项目使用Quasar(Quasar有专属的Capacitor集成方式——请参考),也请跳过此步骤。
package.json@capacitor/corereferences/quasar.md-
安装Capacitor核心库和CLI:bash
npm install @capacitor/core npm install -D @capacitor/cli -
初始化Capacitor:bash
npx cap init当出现提示时,将web目录设置为步骤1中检测到的Vue构建输出路径。对于基于Vite的Vue项目,通常为。dist -
验证生成的或
capacitor.config.ts中的capacitor.config.json值是否与Vue构建输出路径匹配。如果不匹配,请进行更新:webDir:capacitor.config.tstypescriptimport type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'my-app', webDir: 'dist', }; export default config;:capacitor.config.jsonjson{ "appId": "com.example.app", "appName": "my-app", "webDir": "dist" } -
构建Vue应用并添加目标平台:bash
npm run build npm install @capacitor/android @capacitor/ios npx cap add android npx cap add ios npx cap sync
Step 3: Project Structure
步骤3:项目结构
A Capacitor Vue project (Vite-based) has this structure:
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.tsKey points:
- The and
android/directories contain native projects and should be committed to version control.ios/ - The directory contains the Vue app, which is the web layer of the Capacitor app.
src/ - Place custom composables that wrap Capacitor plugins in .
src/composables/ - Capacitor plugins are called from Vue components or composables inside .
src/
基于Vite的Capacitor Vue项目结构如下:
my-app/
├── android/ # Android原生项目
├── ios/ # iOS原生项目
├── public/
├── src/
│ ├── assets/
│ ├── components/
│ ├── composables/ # 封装Capacitor插件的自定义composables
│ ├── router/
│ │ └── index.ts # Vue Router配置文件
│ ├── views/
│ ├── App.vue
│ └── main.ts
├── capacitor.config.ts # 或capacitor.config.json
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts关键要点:
- 和
android/目录包含原生项目,应提交到版本控制系统中。ios/ - 目录包含Vue应用代码,是Capacitor应用的Web层。
src/ - 封装Capacitor插件的自定义composables应放在目录下。
src/composables/ - Capacitor插件应在目录内的Vue组件或composables中调用。
src/
Step 4: Using Capacitor Plugins in Vue
步骤4:在Vue中使用Capacitor插件
Capacitor plugins are plain TypeScript APIs. Import and call them directly in Vue components using the Composition API.
Capacitor插件是纯TypeScript API,可以在Vue组件中直接导入并结合Composition API使用。
Direct Usage in a Component
在组件中直接使用
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>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 }}</p>
<p>经度:{{ longitude }}</p>
<button @click="getCurrentPosition">获取位置</button>
</div>
</template>Wrapping Plugins in Composables (Recommended)
将插件封装到Composables中(推荐)
Wrapping Capacitor plugins in composables provides reusability, encapsulated reactive state, and automatic cleanup:
typescript
// 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,
};
}Use the composable in a component:
vue
<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>将Capacitor插件封装到composables中可以提升复用性、封装响应式状态并实现自动清理:
typescript
// 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,
};
}在组件中使用该composable:
vue
<script setup lang="ts">
import { useCamera } from '@/composables/useCamera';
const { photo, error, takePhoto } = useCamera();
</script>
<template>
<div>
<button @click="takePhoto">拍照</button>
<p v-if="error">错误:{{ error }}</p>
<img v-if="photo?.webPath" :src="photo.webPath" alt="拍摄的照片" />
</div>
</template>Step 5: Plugin Event Listeners with Lifecycle Hooks
步骤5:结合生命周期钩子使用插件事件监听器
Capacitor plugin event listeners must be registered in and removed in to prevent memory leaks. Vue's reactivity system picks up changes automatically, so there is no NgZone-equivalent issue — but cleanup is still critical.
onMountedonUnmountedrefCapacitor插件的事件监听器必须在中注册,并在中移除,以防止内存泄漏。Vue的响应式系统会自动处理的变化,因此不存在类似NgZone的问题,但清理操作仍然至关重要。
onMountedonUnmountedrefComposable with Automatic Cleanup
带有自动清理的Composable
typescript
// 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,
};
}Usage in a component:
vue
<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>typescript
// 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,
};
}在组件中使用:
vue
<script setup lang="ts">
import { useNetwork } from '@/composables/useNetwork';
const { status } = useNetwork();
</script>
<template>
<p v-if="status">网络状态:{{ status.connected ? '在线' : '离线' }}</p>
</template>App-Wide Listeners via App.vue
通过App.vue注册全局监听器
For listeners that should persist for the entire app lifecycle (e.g., app state changes), register them in :
App.vuevue
<!-- src/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>对于需要在整个应用生命周期中持续生效的监听器(如应用状态变化),可以在中注册:
App.vuevue
<!-- src/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('应用状态已变更,当前是否活跃:', state.isActive);
});
});
onUnmounted(async () => {
await appStateListener?.remove();
});
</script>
<template>
<RouterView />
</template>Step 6: Platform Detection
步骤6:平台检测
Use and to conditionally run native-only code. Wrap this in a composable for reuse:
Capacitor.isNativePlatform()Capacitor.getPlatform()typescript
// 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,
};
}Use it in components to show/hide native-only features:
vue
<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>使用和来条件性地运行仅原生环境支持的代码。将其封装到composable中以实现复用:
Capacitor.isNativePlatform()Capacitor.getPlatform()typescript
// 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,
};
}在组件中使用它来显示或隐藏仅原生环境支持的功能:
vue
<script setup lang="ts">
import { usePlatform } from '@/composables/usePlatform';
const { isNative } = usePlatform();
</script>
<template>
<button v-if="isNative" @click="openNativeSettings()">打开设备设置</button>
</template>Step 7: Deep Link Routing with Vue Router
步骤7:结合Vue Router实现深度链接路由
Handle deep links by mapping Capacitor's event to Vue Router navigation. Set this up in or a dedicated composable:
App.addListener('appUrlOpen', ...)App.vuetypescript
// 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();
});
}Use the composable in :
App.vuevue
<!-- src/App.vue -->
<script setup lang="ts">
import { useDeepLinks } from '@/composables/useDeepLinks';
useDeepLinks();
</script>
<template>
<RouterView />
</template>通过将Capacitor的事件映射到Vue Router导航来处理深度链接。可以在或专门的composable中进行配置:
App.addListener('appUrlOpen', ...)App.vuetypescript
// 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;
// 导航到与深度链接路径匹配的路由
// 根据应用的URL scheme调整路径解析逻辑
if (path) {
router.push(path);
}
});
});
onUnmounted(async () => {
await listenerHandle?.remove();
});
}在中使用该composable:
App.vuevue
<!-- src/App.vue -->
<script setup lang="ts">
import { useDeepLinks } from '@/composables/useDeepLinks';
useDeepLinks();
</script>
<template>
<RouterView />
</template>Step 8: Back Button Handling (Android)
步骤8:Android返回按钮处理
Handle the Android hardware back button using combined with Vue Router:
App.addListener('backButton', ...)typescript
// 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();
});
}Use the composable in :
App.vuevue
<!-- src/App.vue -->
<script setup lang="ts">
import { useBackButton } from '@/composables/useBackButton';
useBackButton();
</script>
<template>
<RouterView />
</template>结合和Vue Router来处理Android硬件返回按钮:
App.addListener('backButton', ...)typescript
// 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();
});
}在中使用该composable:
App.vuevue
<!-- src/App.vue -->
<script setup lang="ts">
import { useBackButton } from '@/composables/useBackButton';
useBackButton();
</script>
<template>
<RouterView />
</template>Step 9: PWA Elements Setup
步骤9:PWA Elements配置
Some Capacitor plugins (e.g., Camera, Toast) require for web fallback UI. If the project uses any of these plugins and targets the web:
@ionic/pwa-elements-
Install PWA Elements:bash
npm install @ionic/pwa-elements -
Register the custom elements inbefore
src/main.ts:createApp()typescriptimport { 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');
部分Capacitor插件(如Camera、Toast)需要来提供Web端的 fallback UI。如果项目使用了这些插件且目标平台包含Web:
@ionic/pwa-elements-
安装PWA Elements:bash
npm install @ionic/pwa-elements -
在中在
src/main.ts之前注册自定义元素:createApp()typescriptimport { 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');
Step 10: Build and Sync Workflow
步骤10:构建与同步工作流
After making changes to the Vue app, build and sync to native platforms:
bash
npm run build
npx cap syncTo run on a device or emulator:
bash
npx cap run android
npx cap run iosTo open the native IDE for advanced configuration or debugging:
bash
npx cap open android
npx cap open iosFor live reload during development:
bash
npx cap run android --livereload --external
npx cap run ios --livereload --externalThis starts the Vite dev server internally and configures the native app to load from the development server.
在对Vue应用进行修改后,需要构建并同步到原生平台:
bash
npm run build
npx cap sync要在设备或模拟器上运行应用:
bash
npx cap run android
npx cap run ios要打开原生IDE进行高级配置或调试:
bash
npx cap open android
npx cap open ios开发期间启用热重载:
bash
npx cap run android --livereload --external
npx cap run ios --livereload --external这会在内部启动Vite开发服务器,并配置原生应用从开发服务器加载内容。
Error Handling
错误处理
- mismatch: If
webDircopies the wrong files, verify thatnpx cap syncinwebDirorcapacitor.config.tsmatches the Vue build output path. For Vite-based Vue projects, the default output directory iscapacitor.config.json.dist - Plugin not found at runtime: Run after installing any new plugin. Verify the plugin appears in
npx cap syncdependencies.package.json - Memory leaks from listeners: Always remove plugin listeners in . Store the
onUnmountedreturned byPluginListenerHandleand calladdListeneron unmount.handle.remove() - Deep links not working: Verify the app URL scheme or universal links are configured in the native projects (for Android,
android/app/src/main/AndroidManifest.xmland associated domain entitlement for iOS). Verifyios/App/App/Info.plistis called inuseDeepLinks().App.vue - Back button closes app unexpectedly: Ensure the back button listener checks before calling
canGoBack. Only exit when there is no navigation history.App.exitApp() - PWA Elements not rendering on web: Verify is called in
defineCustomElements(window)beforesrc/main.ts. VerifycreateApp()is installed.@ionic/pwa-elements - Quasar project — Capacitor not detected: Use instead of manually installing Capacitor. See
quasar mode add capacitor.references/quasar.md - Nuxt project — build output not compatible: Nuxt's default SSR output is not compatible with Capacitor. The project must use in
ssr: falseto generate a static SPA. Seenuxt.config.ts.references/nuxt.md
- 不匹配:如果
webDir复制了错误的文件,请验证npx cap sync或capacitor.config.ts中的capacitor.config.json是否与Vue构建输出路径匹配。对于基于Vite的Vue项目,默认输出目录是webDir。dist - 运行时找不到插件:安装新插件后请运行。验证插件是否出现在
npx cap sync的依赖中。package.json - 监听器导致内存泄漏:务必在中移除插件监听器。保存
onUnmounted返回的addListener,并在卸载时调用PluginListenerHandle。handle.remove() - 深度链接无法工作:验证原生项目中是否配置了应用URL scheme或通用链接(Android为,iOS为
android/app/src/main/AndroidManifest.xml及关联域权限)。验证ios/App/App/Info.plist中是否调用了App.vue。useDeepLinks() - 返回按钮意外关闭应用:确保返回按钮监听器在调用之前检查
App.exitApp()。仅当没有导航历史时才退出应用。canGoBack - Web端PWA Elements无法渲染:验证中是否在
src/main.ts之前调用了createApp()。验证是否已安装defineCustomElements(window)。@ionic/pwa-elements - Quasar项目无法检测到Capacitor:使用替代手动安装Capacitor,请参考
quasar mode add capacitor。references/quasar.md - Nuxt项目构建输出不兼容:Nuxt默认的SSR输出与Capacitor不兼容。项目必须在中设置
nuxt.config.ts以生成静态SPA,请参考ssr: false。references/nuxt.md
Related Skills
相关技能
- — Create a new Capacitor app from scratch.
capacitor-app-creation - — General Capacitor development guidance not specific to Vue.
capacitor-app-development - — Install and configure Capacitor plugins from official and community sources.
capacitor-plugins - — Ionic Framework with Vue (UI components, navigation, theming on top of Capacitor).
ionic-vue - — Upgrade a Capacitor app to a newer major version.
capacitor-app-upgrades
- —— 从头创建新的Capacitor应用。
capacitor-app-creation - —— 非Vue专属的通用Capacitor开发指导。
capacitor-app-development - —— 安装和配置官方及社区提供的Capacitor插件。
capacitor-plugins - —— Ionic Framework与Vue结合(基于Capacitor的UI组件、导航、主题设置)。
ionic-vue - —— 将Capacitor应用升级到更高的主版本。",
capacitor-app-upgrades