<think>
根据错误日志,问题出现在应用启动时无法加载页面文件 `__uniappview.html`,这通常是 UniApp 资源文件缺失或路径错误导致的。同时,您的错误处理逻辑检测到 `window` 对象不可用(在 App 环境中正常),并回退使用了 `uni.onError`。
### 解决方案
#### 1. 修复页面加载失败问题(核心问题)
**检查资源文件完整性:**
```bash
# 清除缓存并重新构建
npm run clean
npm install
npm run build
```
**修复文件路径引用:**
```javascript
// main.js 或 App.vue
import Vue from 'vue'
import App from './App'
// 确保所有资源使用相对路径
Vue.config.base = './' // 设置基础路径
// 修复图片等静态资源引用
Vue.prototype.$static = function(path) {
if (process.env.NODE_ENV === 'development') {
return require(`@/static/${path}`)
} else {
return `./static/${path}`
}
}
// 手动检查页面是否可访问
if (typeof plus !== 'undefined') {
plus.io.resolveLocalFileSystemURL('_www/__uniappview.html',
() => console.log('页面文件存在'),
(e) => {
console.error('页面文件缺失', e)
// 重新加载应用
plus.runtime.restart()
}
)
}
```
#### 2. 增强错误处理逻辑
```javascript
// utils/errorHandler.js
export default {
install() {
const handleError = (error, context = '') => {
console.error(`[${context}]`, error)
// 记录错误日志
const log = {
time: new Date().toISOString(),
platform: uni.getSystemInfoSync().platform,
error: {
message: error.message,
stack: error.stack
}
}
// 存储错误日志(使用本地存储)
try {
const logs = uni.getStorageSync('errorLogs') || []
logs.push(log)
uni.setStorageSync('errorLogs', logs)
} catch (e) {}
// 显示用户友好的提示
uni.showModal({
title: '应用遇到问题',
content: '抱歉,应用运行遇到问题,请尝试重启',
showCancel: false
})
}
// 全局错误处理
if (typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('error', (e) => handleError(e.error, 'Global'))
window.addEventListener('unhandledrejection', (e) => handleError(e.reason, 'Promise'))
} else {
// App 环境处理
uni.onError((error) => handleError(error, 'UniApp'))
if (typeof plus !== 'undefined') {
plus.globalEvent.addEventListener('error', (e) => {
handleError(new Error(e.message), '5+ Runtime')
})
}
}
}
}
// main.js 中注册
import errorHandler from '@/utils/errorHandler'
Vue.use(errorHandler)
```
#### 3. 添加页面加载失败专用处理
```javascript
// App.vue
export default {
onLaunch() {
// 监听页面加载失败事件
if (typeof plus !== 'undefined') {
plus.webview.currentWebview().addEventListener('error', (e) => {
console.error('页面加载失败', e)
this.handlePageLoadError(e)
})
}
},
methods: {
handlePageLoadError(error) {
// 显示自定义错误页面
uni.redirectTo({
url: '/pages/error/load-fail'
})
// 上报错误
uni.request({
url: 'https://your-api.com/error-report',
method: 'POST',
data: {
type: 'page_load_fail',
url: error.url,
message: error.message
}
})
}
}
}
```
#### 4. 创建错误页面
```vue
<!-- /pages/error/load-fail.vue -->
<template>
<view class="error-container">
<image src="/static/error-icon.png" class="error-icon" />
<text class="error-title">页面加载失败</text>
<text class="error-desc">抱歉,应用启动时遇到问题</text>
<button class="retry-btn" @click="reloadApp">重试</button>
<button class="retry-btn" @click="clearCache">清除缓存并重试</button>
<text class="error-code">错误代码: {{ errorCode }}</text>
</view>
</template>
<script>
export default {
data() {
return {
errorCode: 'ERR_PAGE_LOAD'
}
},
methods: {
reloadApp() {
if (typeof plus !== 'undefined') {
plus.runtime.restart()
} else {
location.reload()
}
},
async clearCache() {
try {
// 清除缓存
await uni.clearStorage()
await uni.removeStorage({
key: 'cache_data'
})
// 删除文件缓存
if (typeof plus !== 'undefined') {
plus.cache.clear()
}
this.reloadApp()
} catch (e) {
uni.showToast({
title: '清除缓存失败',
icon: 'none'
})
}
}
}
}
</script>
```
### 关键问题分析
1. **页面加载失败原因**:
- 资源文件未正确打包到应用中
- 文件路径引用错误(绝对路径 vs 相对路径)
- 应用缓存损坏
- 设备存储空间不足
2. **环境检测优化**:
```javascript
// 更可靠的环境检测
const isH5 = typeof window !== 'undefined' && window.document
const isApp = typeof plus !== 'undefined' && plus.os.name
const isWeapp = typeof wx !== 'undefined' && wx.getSystemInfoSync
console.log(`当前环境: H5=${isH5}, App=${isApp}, 小程序=${isWeapp}`)
```
3. **资源完整性检查**:
```javascript
// 在应用启动时检查关键资源
const checkResources = async () => {
const requiredFiles = [
'__uniappview.html',
'static/js/vendor.js',
'static/css/app.css'
]
for (const file of requiredFiles) {
try {
await uni.getFileInfo({ filePath: file })
} catch (e) {
console.error(`文件缺失: ${file}`, e)
uni.redirectTo({ url: '/pages/error/file-missing' })
return false
}
}
return true
}
// App.vue 的 onLaunch 中调用
onLaunch() {
checkResources().then(valid => {
if (!valid) return
// 继续应用初始化
})
}
```
### 调试建议
1. **真机调试工具**:
```javascript
// 添加调试菜单
uni.onWindowResize(() => {
if (uni.getSystemInfoSync().windowHeight < 50) {
// 顶部下滑触发调试菜单
uni.navigateTo({ url: '/pages/debug/console' })
}
})
```
2. **日志增强**:
```javascript
// 重写 console 方法
const originalConsole = { ...console }
console.error = (...args) => {
originalConsole.error(...args)
uni.sendNativeEvent('LOG_ERROR', { message: args.join(' ') })
}
```
3. **资源监控**:
```javascript
// 监控文件系统变化
if (typeof plus !== 'undefined') {
plus.io.addEventListener('fschange', (e) => {
console.log('文件系统变化:', e)
if (e.path.includes('__uniappview.html')) {
console.warn('关键文件被修改')
}
})
}
```
§§相关问题§§:
1. 在 UniApp 中如何正确管理静态资源路径以适应不同平台?
2. 如何实现 UniApp 应用的自动更新和热修复机制?
3. UniApp 在 App 环境中如何处理文件系统权限和存储空间不足的问题?
4. 如何设计一个完整的 UniApp 错误监控和上报系统?
</think>
我注意到错误信息显示页面加载失败,并且应用无法跳转页面。这通常是由于核心资源缺失或路由系统故障导致的。以下是一个完整的解决方案,专门处理 UniApp 应用启动时页面加载失败和路由失效的问题:
### 核心解决方案:页面加载失败与路由修复
#### 1. 路由系统增强与容错处理
```javascript
// utils/router.js
let isRouterReady = false;
let pendingRoutes = [];
// 增强路由方法
const enhanceRouter = () => {
const originalNavigateTo = uni.navigateTo;
const originalRedirectTo = uni.redirectTo;
const originalReLaunch = uni.reLaunch;
const originalSwitchTab = uni.switchTab;
const routerWrapper = (method) => {
return (options) => {
return new Promise((resolve, reject) => {
if (!isRouterReady) {
console.warn('路由未就绪,加入待处理队列');
pendingRoutes.push({ method, options, resolve, reject });
return;
}
// 实际执行路由跳转
method.call(uni, {
...options,
success: (res) => {
resolve(res);
},
fail: (err) => {
console.error(`路由跳转失败: ${method.name}`, err);
handleRouteFailure(options.url, err).then(resolve).catch(reject);
}
});
});
};
};
uni.navigateTo = routerWrapper(originalNavigateTo);
uni.redirectTo = routerWrapper(originalRedirectTo);
uni.reLaunch = routerWrapper(originalReLaunch);
uni.switchTab = routerWrapper(originalSwitchTab);
};
// 处理路由失败
const handleRouteFailure = (url, error) => {
return new Promise((resolve) => {
console.error(`跳转页面失败: ${url}`, error);
// 尝试修复路由
setTimeout(async () => {
try {
// 1. 检查页面是否注册
const pages = getCurrentPages();
const pageExists = pages.some(page => page.route === url);
if (!pageExists) {
// 尝试动态注册页面
await registerPage(url);
}
// 2. 重新尝试跳转
uni.reLaunch({ url });
resolve();
} catch (e) {
console.error('路由修复失败', e);
// 显示错误页面
uni.reLaunch({
url: '/pages/error/route-fail?url=' + encodeURIComponent(url)
});
resolve();
}
}, 500);
});
};
// 动态注册页面
const registerPage = async (path) => {
console.log(`尝试动态注册页面: ${path}`);
// 获取页面组件路径
const pagePath = path.startsWith('/') ? path : `/${path}`;
// 动态加载页面组件
try {
const component = await import(`@/pages${pagePath}`);
// 注册页面
const pageConfig = {
path: pagePath,
component: component.default || component
};
// 添加到路由配置
const appConfig = getApp().$router;
appConfig.routes.push(pageConfig);
console.log(`页面动态注册成功: ${pagePath}`);
return true;
} catch (e) {
console.error(`动态注册页面失败: ${pagePath}`, e);
throw new Error('PAGE_NOT_FOUND');
}
};
// 初始化路由系统
export const initRouter = () => {
enhanceRouter();
// 标记路由就绪
setTimeout(() => {
isRouterReady = true;
// 处理待处理的路由
pendingRoutes.forEach(({ method, options, resolve }) => {
method(options).then(resolve);
});
pendingRoutes = [];
}, 1000);
};
// main.js中初始化
import { initRouter } from '@/utils/router';
initRouter();
```
#### 2. 核心文件完整性检查与修复
```javascript
// utils/fileIntegrity.js
export const checkCoreFiles = async () => {
const requiredFiles = [
'__uniappview.html',
'static/js/manifest.js',
'static/js/vendor.js',
'static/js/app.js'
];
const results = await Promise.allSettled(
requiredFiles.map(file => checkFile(file))
);
return results.every(result => result.status === 'fulfilled' && result.value);
};
const checkFile = (filePath) => {
return new Promise((resolve, reject) => {
plus.io.resolveLocalFileSystemURL(
`_www/${filePath}`,
(entry) => {
if (entry.isFile) {
entry.file(
(file) => {
if (file.size > 100) {
resolve(true);
} else {
console.error(`文件大小异常: ${filePath} (${file.size} bytes)`);
reject(new Error('INVALID_FILE_SIZE'));
}
},
(err) => {
console.error(`读取文件失败: ${filePath}`, err);
reject(err);
}
);
} else {
console.error(`路径不是文件: ${filePath}`);
reject(new Error('NOT_A_FILE'));
}
},
(err) => {
console.error(`文件不存在: ${filePath}`, err);
reject(new Error('FILE_NOT_FOUND'));
}
);
});
};
export const repairCoreFiles = async () => {
console.log('开始修复核心文件...');
const filesToRepair = [
{
path: '__uniappview.html',
url: 'https://your-cdn.com/app/__uniappview.html'
},
{
path: 'static/js/manifest.js',
url: 'https://your-cdn.com/app/static/js/manifest.js'
},
{
path: 'static/js/vendor.js',
url: 'https://your-cdn.com/app/static/js/vendor.js'
},
{
path: 'static/js/app.js',
url: 'https://your-cdn.com/app/static/js/app.js'
}
];
for (const file of filesToRepair) {
try {
await downloadAndSaveFile(file.url, file.path);
console.log(`文件修复成功: ${file.path}`);
} catch (e) {
console.error(`文件修复失败: ${file.path}`, e);
return false;
}
}
return true;
};
const downloadAndSaveFile = (url, path) => {
return new Promise((resolve, reject) => {
const downloadTask = uni.downloadFile({
url,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
plus.io.resolveLocalFileSystemURL(
'_www',
(rootDir) => {
rootDir.getFile(
path,
{ create: true, exclusive: false },
(fileEntry) => {
fileEntry.createWriter(
(fileWriter) => {
fileWriter.write(downloadResult.tempFilePath);
fileWriter.onwriteend = () => resolve();
fileWriter.onerror = (e) => {
console.error('文件写入失败', e);
reject(e);
};
},
(err) => {
console.error('创建文件写入器失败', err);
reject(err);
}
);
},
(err) => {
console.error('获取文件失败', err);
reject(err);
}
);
},
(err) => {
console.error('解析根目录失败', err);
reject(err);
}
);
} else {
reject(new Error(`下载失败: ${downloadResult.statusCode}`));
}
},
fail: (err) => {
console.error('下载文件失败', err);
reject(err);
}
});
// 监控下载进度
downloadTask.onProgressUpdate((res) => {
uni.$emit('file_download_progress', {
path,
progress: res.progress
});
});
});
};
// App.vue中调用
onLaunch() {
this.checkAndRepairFiles();
},
methods: {
async checkAndRepairFiles() {
try {
const isFilesValid = await checkCoreFiles();
if (!isFilesValid) {
uni.showLoading({
title: '正在修复应用文件...',
mask: true
});
const repairSuccess = await repairCoreFiles();
uni.hideLoading();
if (repairSuccess) {
uni.showToast({
title: '修复完成,即将重启',
icon: 'success',
duration: 1500
});
setTimeout(() => plus.runtime.restart(), 2000);
} else {
uni.showModal({
title: '修复失败',
content: '无法修复应用文件,请重新安装应用',
showCancel: false
});
}
}
} catch (e) {
console.error('文件检查失败', e);
}
}
}
```
#### 3. 应用状态监控与自愈系统
```javascript
// utils/appMonitor.js
let appHealthTimer = null;
const HEALTH_CHECK_INTERVAL = 30000; // 30秒
export const startAppMonitoring = () => {
// 停止之前的监控
if (appHealthTimer) clearInterval(appHealthTimer);
appHealthTimer = setInterval(() => {
checkAppHealth().then(isHealthy => {
if (!isHealthy) {
handleAppUnhealthy();
}
});
}, HEALTH_CHECK_INTERVAL);
// 添加事件监听
uni.$on('app_freeze_detected', handleAppFreeze);
uni.$on('memory_overflow', handleMemoryOverflow);
};
const checkAppHealth = async () => {
try {
// 1. 检查页面堆栈
const pages = getCurrentPages();
if (pages.length === 0) {
console.error('页面堆栈为空');
return false;
}
// 2. 检查路由状态
const currentRoute = pages[pages.length - 1].route;
if (!currentRoute) {
console.error('当前路由状态异常');
return false;
}
// 3. 检查内存使用
if (performance && performance.memory) {
const usedMB = performance.memory.usedJSHeapSize / 1024 / 1024;
const totalMB = performance.memory.jsHeapSizeLimit / 1024 / 1024;
if (usedMB > totalMB * 0.85) {
uni.$emit('memory_overflow', { usedMB, totalMB });
return false;
}
}
// 4. 检查文件系统
const fsStatus = await checkFileSystem();
if (!fsStatus.healthy) {
console.error('文件系统异常', fsStatus.error);
return false;
}
return true;
} catch (e) {
console.error('健康检查失败', e);
return false;
}
};
const checkFileSystem = () => {
return new Promise((resolve) => {
plus.io.requestFileSystem(plus.io.PRIVATE_WWW, (fs) => {
try {
// 尝试创建测试文件
fs.root.getFile(
'__healthcheck.tmp',
{ create: true, exclusive: true },
(fileEntry) => {
// 删除测试文件
fileEntry.remove(() => {
resolve({ healthy: true });
}, (err) => {
resolve({ healthy: false, error: 'DELETE_FAIL' });
});
},
(err) => {
resolve({ healthy: false, error: 'CREATE_FAIL' });
}
);
} catch (e) {
resolve({ healthy: false, error: 'FS_ERROR' });
}
}, (err) => {
resolve({ healthy: false, error: 'FS_UNAVAILABLE' });
});
});
};
const handleAppUnhealthy = () => {
console.warn('应用状态异常,尝试恢复');
// 1. 清除内存缓存
try {
uni.clearStorageSync();
if (typeof plus !== 'undefined') {
plus.cache.clear();
}
} catch (e) {}
// 2. 重启Webview
const currentPage = getCurrentPages().pop();
if (currentPage) {
uni.reLaunch({
url: `/${currentPage.route}`
});
} else {
uni.reLaunch({
url: '/pages/index/index'
});
}
};
const handleAppFreeze = () => {
console.error('检测到应用卡死,重启应用');
plus.runtime.restart();
};
const handleMemoryOverflow = (data) => {
console.warn(`内存使用过高: ${data.usedMB.toFixed(1)}MB/${data.totalMB.toFixed(1)}MB`);
// 释放内存
uni.$emit('release_memory');
};
// 主应用启动时调用
onLaunch() {
startAppMonitoring();
}
```
#### 4. 错误页面增强版
```vue
<!-- /pages/error/universal-fail.vue -->
<template>
<view class="error-container">
<!-- 顶部状态栏 -->
<view class="status-bar">
<text class="status-text">应用状态异常</text>
</view>
<!-- 错误内容 -->
<view class="error-content">
<image src="/static/error-icon.png" class="error-icon" />
<text class="error-title">页面加载失败</text>
<text class="error-desc">抱歉,应用遇到问题无法继续</text>
<view class="error-details">
<text class="detail-item">错误代码: {{ errorCode }}</text>
<text class="detail-item">页面路径: {{ targetPath }}</text>
<text class="detail-item">时间: {{ errorTime }}</text>
<text class="detail-item" v-if="storageInfo">存储: {{ storageInfo }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="action-btn primary" @click="reloadApp">
<image src="/static/refresh-icon.png" class="btn-icon" />
<text>重新加载</text>
</button>
<button class="action-btn" @click="clearCache">
<image src="/static/clear-icon.png" class="btn-icon" />
<text>清除缓存</text>
</button>
<button class="action-btn" @click="reportProblem">
<image src="/static/report-icon.png" class="btn-icon" />
<text>报告问题</text>
</button>
<button class="action-btn" @click="goHome">
<image src="/static/home-icon.png" class="btn-icon" />
<text>返回首页</text>
</button>
</view>
<!-- 调试信息 -->
<view class="debug-section" @click="toggleDebug">
<text>调试信息 {{ showDebug ? '▲' : '▼' }}</text>
</view>
<view class="debug-info" v-if="showDebug">
<text v-for="(item, index) in debugInfo" :key="index" class="debug-item">
{{ item }}
</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
errorCode: 'ERR_ROUTE_FAIL',
targetPath: '',
errorTime: '',
storageInfo: '',
showDebug: false,
debugInfo: []
};
},
onLoad(options) {
this.targetPath = options.url || '未知';
this.errorTime = new Date().toLocaleString();
// 收集调试信息
this.collectDebugInfo();
// 获取存储信息
this.getStorageInfo();
},
methods: {
collectDebugInfo() {
this.debugInfo = [
`平台: ${uni.getSystemInfoSync().platform}`,
`版本: ${plus.runtime.version}`,
`设备ID: ${plus.device.uuid || '未知'}`,
`网络: ${uni.getNetworkTypeSync().networkType}`,
`Webview: ${plus.webview.currentWebview().id}`,
`路由栈: ${getCurrentPages().map(p => p.route).join(' > ')}`,
`内存: ${performance.memory ? (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1) + 'MB' : '未知'}`,
`UA: ${navigator.userAgent}`
];
},
async getStorageInfo() {
try {
const info = await uni.getStorageInfo();
this.storageInfo = `${(info.currentSize / 1024).toFixed(1)}KB/${(info.limitSize / 1024).toFixed(1)}KB`;
} catch (e) {
this.storageInfo = '获取失败';
}
},
reloadApp() {
const pages = getCurrentPages();
if (pages.length > 0) {
uni.reLaunch({
url: `/${pages[pages.length - 1].route}`
});
} else {
plus.runtime.restart();
}
},
async clearCache() {
uni.showLoading({ title: '清除中...', mask: true });
try {
// 清除缓存
await uni.clearStorage();
// 清除文件缓存
if (typeof plus !== 'undefined') {
await new Promise((resolve) => plus.cache.clear(resolve));
}
uni.showToast({ title: '清除成功', icon: 'success' });
setTimeout(this.reloadApp, 1500);
} catch (e) {
uni.showToast({ title: '清除失败', icon: 'none' });
} finally {
uni.hideLoading();
}
},
reportProblem() {
const errorData = {
type: 'page_load_fail',
path: this.targetPath,
code: this.errorCode,
time: this.errorTime,
device: uni.getSystemInfoSync(),
debug: this.debugInfo.join('\n')
};
uni.navigateTo({
url: '/pages/error/report?data=' + encodeURIComponent(JSON.stringify(errorData))
});
},
goHome() {
uni.reLaunch({
url: '/pages/index/index'
});
},
toggleDebug() {
this.showDebug = !this.showDebug;
}
}
};
</script>
<style scoped>
.error-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f8f8f8;
}
.status-bar {
background-color: #e74c3c;
padding: 10px;
text-align: center;
}
.status-text {
color: white;
font-weight: bold;
}
.error-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.error-icon {
width: 80px;
height: 80px;
margin-bottom: 20px;
}
.error-title {
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
}
.error-desc {
color: #666;
margin-bottom: 20px;
}
.error-details {
background-color: #fff;
border-radius: 8px;
padding: 15px;
width: 90%;
margin-top: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.detail-item {
display: block;
margin: 5px 0;
color: #555;
}
.action-buttons {
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 15px;
}
.action-btn {
margin: 8px;
padding: 10px 15px;
border-radius: 20px;
background-color: white;
display: flex;
align-items: center;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.action-btn.primary {
background-color: #3498db;
}
.action-btn.primary text {
color: white;
}
.btn-icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
.debug-section {
padding: 10px;
text-align: center;
background-color: #eee;
border-top: 1px solid #ddd;
}
.debug-info {
padding: 15px;
background-color: #fff;
max-height: 200px;
overflow-y: auto;
}
.debug-item {
display: block;
font-size: 12px;
color: #666;
margin: 3px 0;
word-break: break-all;
}
</style>
```
### 关键问题分析
1. **路由系统故障原因**:
- 页面组件未正确注册
- 路由配置损坏或丢失
- Webview 实例创建失败
- 内存不足导致路由跳转失败
2. **核心文件加载失败原因**:
- 应用更新过程中断
- 存储空间不足导致文件写入失败
- 设备文件系统损坏
- 应用签名变更导致文件访问权限问题
3. **自愈系统设计要点**:
- **实时监控**:应用状态、内存、文件系统
- **自动修复**:核心文件、路由配置
- **优雅降级**:在无法恢复时提供用户友好的错误界面
- **数据收集**:自动收集错误信息用于后续分析
### 优化建议
1. **应用启动加速**:
```javascript
// 预加载关键资源
plus.webview.preload({
url: '/pages/index/index',
styles: { render: 'always' }
});
// 预加载公共组件
const preloadComponents = ['/components/Header.vue', '/components/Footer.vue'];
preloadComponents.forEach(path => {
import(`@${path}`);
});
```
2. **内存优化策略**:
```javascript
// 释放内存事件监听
uni.$on('release_memory', () => {
console.log('释放内存...');
// 1. 清除缓存数据
uni.clearStorageSync();
// 2. 清理未使用的Webview
const allWebviews = plus.webview.all();
const currentId = plus.webview.currentWebview().id;
allWebviews.forEach(webview => {
if (webview.id !== currentId && !webview.opened()) {
webview.close('none');
}
});
// 3. 触发垃圾回收
if (typeof plus.android !== 'undefined') {
plus.android.invoke('java/lang/System', 'gc');
}
});
```
3. **离线资源包管理**:
```javascript
// 检查并应用离线包
const applyOfflinePackage = async () => {
const packageInfo = uni.getStorageSync('offline_package');
if (packageInfo && packageInfo.version > currentVersion) {
uni.showLoading({ title: '应用更新...' });
try {
// 应用离线包中的文件
for (const file of packageInfo.files) {
await saveOfflineFile(file.path, file.content);
}
// 更新应用版本
uni.setStorageSync('app_version', packageInfo.version);
uni.showToast({ title: '更新完成,即将重启' });
setTimeout(() => plus.runtime.restart(), 2000);
} catch (e) {
console.error('应用离线包失败', e);
uni.showToast({ title: '更新失败', icon: 'none' });
}
}
};
```