Skip to content

API FAQ

Overview

This FAQ covers common questions and issues related to uni-app APIs, including platform-specific behaviors, best practices, and troubleshooting tips.

General API Questions

Q: What's the difference between uni.request and native fetch/XMLHttpRequest?

A: uni.request is uni-app's unified network request API that works across all platforms:

javascript
// uni.request - Recommended for uni-app
uni.request({
  url: 'https://api.example.com/data',
  method: 'GET',
  data: {
    id: 123
  },
  success: (res) => {
    console.log(res.data)
  },
  fail: (err) => {
    console.error(err)
  }
})

// Promise-based approach
const response = await uni.request({
  url: 'https://api.example.com/data',
  method: 'POST',
  data: {
    name: 'John',
    email: 'john@example.com'
  }
})

Benefits of uni.request:

  • Cross-platform compatibility
  • Automatic request/response interceptors
  • Built-in error handling
  • Platform-specific optimizations

Q: How do I handle API responses consistently across platforms?

A: Use a unified response handler:

javascript
// utils/api.js
export const apiRequest = (options) => {
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      success: (res) => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          resolve(res.data)
        } else {
          reject(new Error(`HTTP ${res.statusCode}: ${res.data.message || 'Request failed'}`))
        }
      },
      fail: (err) => {
        reject(new Error(`Network error: ${err.errMsg}`))
      }
    })
  })
}

// Usage
try {
  const data = await apiRequest({
    url: '/api/users',
    method: 'GET'
  })
  console.log(data)
} catch (error) {
  console.error('API Error:', error.message)
}

Q: How do I implement request interceptors?

A: Create a request wrapper with interceptor functionality:

javascript
// utils/request.js
class RequestInterceptor {
  constructor() {
    this.requestInterceptors = []
    this.responseInterceptors = []
  }
  
  // Add request interceptor
  addRequestInterceptor(interceptor) {
    this.requestInterceptors.push(interceptor)
  }
  
  // Add response interceptor
  addResponseInterceptor(interceptor) {
    this.responseInterceptors.push(interceptor)
  }
  
  // Make request with interceptors
  async request(config) {
    // Apply request interceptors
    let finalConfig = config
    for (const interceptor of this.requestInterceptors) {
      finalConfig = await interceptor(finalConfig)
    }
    
    try {
      const response = await uni.request(finalConfig)
      
      // Apply response interceptors
      let finalResponse = response
      for (const interceptor of this.responseInterceptors) {
        finalResponse = await interceptor(finalResponse)
      }
      
      return finalResponse
    } catch (error) {
      throw error
    }
  }
}

// Create instance
const request = new RequestInterceptor()

// Add token to all requests
request.addRequestInterceptor((config) => {
  const token = uni.getStorageSync('token')
  if (token) {
    config.header = {
      ...config.header,
      'Authorization': `Bearer ${token}`
    }
  }
  return config
})

// Handle response errors globally
request.addResponseInterceptor((response) => {
  if (response.statusCode === 401) {
    // Redirect to login
    uni.navigateTo({
      url: '/pages/login/login'
    })
  }
  return response
})

Storage APIs

Q: What's the difference between sync and async storage methods?

A: Synchronous methods block execution, while asynchronous methods don't:

javascript
// Synchronous - blocks execution
try {
  uni.setStorageSync('key', 'value')
  const value = uni.getStorageSync('key')
  console.log(value) // 'value'
} catch (error) {
  console.error('Storage error:', error)
}

// Asynchronous - non-blocking
uni.setStorage({
  key: 'key',
  data: 'value',
  success: () => {
    uni.getStorage({
      key: 'key',
      success: (res) => {
        console.log(res.data) // 'value'
      }
    })
  }
})

// Promise-based async approach
try {
  await uni.setStorage({
    key: 'key',
    data: 'value'
  })
  
  const res = await uni.getStorage({
    key: 'key'
  })
  console.log(res.data) // 'value'
} catch (error) {
  console.error('Storage error:', error)
}

When to use each:

  • Use sync methods for simple, quick operations
  • Use async methods for large data or when you need non-blocking behavior

Q: How do I handle storage quota limits?

A: Implement storage management with quota checking:

javascript
// utils/storage.js
export const StorageManager = {
  // Check available storage space
  async getStorageInfo() {
    try {
      const info = await uni.getStorageInfo()
      return {
        keys: info.keys,
        currentSize: info.currentSize,
        limitSize: info.limitSize,
        usage: (info.currentSize / info.limitSize * 100).toFixed(2) + '%'
      }
    } catch (error) {
      console.error('Failed to get storage info:', error)
      return null
    }
  },
  
  // Clean up old data
  async cleanupStorage(maxAge = 7 * 24 * 60 * 60 * 1000) { // 7 days
    try {
      const info = await uni.getStorageInfo()
      const now = Date.now()
      
      for (const key of info.keys) {
        if (key.startsWith('cache_')) {
          const data = uni.getStorageSync(key)
          if (data && data.timestamp && (now - data.timestamp) > maxAge) {
            uni.removeStorageSync(key)
          }
        }
      }
    } catch (error) {
      console.error('Storage cleanup failed:', error)
    }
  },
  
  // Set data with expiration
  setWithExpiration(key, data, expirationMs) {
    const item = {
      data,
      timestamp: Date.now(),
      expiration: expirationMs
    }
    
    try {
      uni.setStorageSync(key, item)
    } catch (error) {
      if (error.errMsg.includes('exceed max storage size')) {
        // Clean up and retry
        this.cleanupStorage()
        uni.setStorageSync(key, item)
      }
    }
  },
  
  // Get data with expiration check
  getWithExpiration(key) {
    try {
      const item = uni.getStorageSync(key)
      if (!item) return null
      
      const now = Date.now()
      if (item.expiration && (now - item.timestamp) > item.expiration) {
        uni.removeStorageSync(key)
        return null
      }
      
      return item.data
    } catch (error) {
      console.error('Failed to get storage item:', error)
      return null
    }
  }
}

Q: What's the difference between navigateTo, redirectTo, and reLaunch?

A: Each navigation method serves different purposes:

javascript
// navigateTo - Add to navigation stack (can go back)
uni.navigateTo({
  url: '/pages/detail/detail?id=123'
})

// redirectTo - Replace current page (cannot go back)
uni.redirectTo({
  url: '/pages/result/result'
})

// reLaunch - Clear stack and navigate to new page
uni.reLaunch({
  url: '/pages/home/home'
})

// switchTab - Navigate to tab page
uni.switchTab({
  url: '/pages/profile/profile'
})

// navigateBack - Go back in navigation stack
uni.navigateBack({
  delta: 1 // Number of pages to go back
})

Use cases:

  • navigateTo: Normal page navigation
  • redirectTo: Login success, form submission results
  • reLaunch: App restart, major state changes
  • switchTab: Tab bar navigation
  • navigateBack: Return to previous page

Q: How do I pass complex data between pages?

A: Several approaches for passing data:

javascript
// Method 1: URL parameters (for simple data)
uni.navigateTo({
  url: `/pages/detail/detail?id=${id}&name=${encodeURIComponent(name)}`
})

// Method 2: Global storage (for complex data)
// Page A
uni.setStorageSync('pageData', {
  user: userObject,
  settings: settingsObject
})
uni.navigateTo({
  url: '/pages/detail/detail'
})

// Page B
const pageData = uni.getStorageSync('pageData')

// Method 3: Event bus (for real-time updates)
// utils/eventBus.js
export const EventBus = {
  events: {},
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
  },
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data))
    }
  },
  
  off(event, callback) {
    if (this.events[event]) {
      const index = this.events[event].indexOf(callback)
      if (index > -1) {
        this.events[event].splice(index, 1)
      }
    }
  }
}

// Usage
import { EventBus } from '@/utils/eventBus'

// Page A
EventBus.emit('dataUpdate', complexData)

// Page B
EventBus.on('dataUpdate', (data) => {
  console.log('Received data:', data)
})

File System APIs

Q: How do I handle file uploads with progress tracking?

A: Implement file upload with progress monitoring:

javascript
// utils/fileUpload.js
export const FileUploader = {
  // Upload single file with progress
  uploadFile(filePath, options = {}) {
    return new Promise((resolve, reject) => {
      const uploadTask = uni.uploadFile({
        url: options.url || '/api/upload',
        filePath: filePath,
        name: options.name || 'file',
        formData: options.formData || {},
        header: options.header || {},
        success: (res) => {
          try {
            const data = JSON.parse(res.data)
            resolve(data)
          } catch (error) {
            resolve(res.data)
          }
        },
        fail: reject
      })
      
      // Track upload progress
      uploadTask.onProgressUpdate((progress) => {
        const percent = Math.round(progress.progress)
        if (options.onProgress) {
          options.onProgress(percent, progress)
        }
      })
      
      // Allow cancellation
      if (options.onTaskCreated) {
        options.onTaskCreated(uploadTask)
      }
    })
  },
  
  // Upload multiple files
  async uploadMultipleFiles(filePaths, options = {}) {
    const results = []
    const errors = []
    
    for (let i = 0; i < filePaths.length; i++) {
      try {
        const result = await this.uploadFile(filePaths[i], {
          ...options,
          onProgress: (percent, progress) => {
            if (options.onProgress) {
              options.onProgress(i, percent, progress)
            }
          }
        })
        results.push(result)
      } catch (error) {
        errors.push({ index: i, error })
      }
    }
    
    return { results, errors }
  },
  
  // Compress image before upload
  async compressAndUpload(imagePath, options = {}) {
    try {
      // Compress image
      const compressedPath = await uni.compressImage({
        src: imagePath,
        quality: options.quality || 80,
        width: options.width,
        height: options.height
      })
      
      // Upload compressed image
      return await this.uploadFile(compressedPath.tempFilePath, options)
    } catch (error) {
      console.error('Compress and upload failed:', error)
      throw error
    }
  }
}

// Usage example
import { FileUploader } from '@/utils/fileUpload'

// In component
methods: {
  async handleFileUpload() {
    try {
      // Choose image
      const chooseResult = await uni.chooseImage({
        count: 1,
        sizeType: ['compressed']
      })
      
      const filePath = chooseResult.tempFilePaths[0]
      
      // Upload with progress
      const result = await FileUploader.compressAndUpload(filePath, {
        url: '/api/upload/image',
        quality: 70,
        onProgress: (percent) => {
          this.uploadProgress = percent
        }
      })
      
      console.log('Upload successful:', result)
    } catch (error) {
      console.error('Upload failed:', error)
      uni.showToast({
        title: 'Upload failed',
        icon: 'none'
      })
    }
  }
}

Platform-Specific API Issues

Q: How do I handle platform differences in APIs?

A: Use conditional compilation and platform detection:

javascript
// Platform detection
const platform = uni.getSystemInfoSync().platform

// Conditional compilation
// #ifdef MP-WEIXIN
// WeChat Mini Program specific code
wx.getLocation({
  type: 'gcj02',
  success: (res) => {
    console.log('WeChat location:', res)
  }
})
// #endif

// #ifdef APP-PLUS
// App specific code
plus.geolocation.getCurrentPosition((position) => {
  console.log('App location:', position)
})
// #endif

// #ifdef H5
// Web specific code
if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition((position) => {
    console.log('Web location:', position)
  })
}
// #endif

// Universal approach with fallbacks
export const LocationService = {
  async getCurrentLocation() {
    return new Promise((resolve, reject) => {
      uni.getLocation({
        type: 'gcj02',
        success: (res) => {
          resolve({
            latitude: res.latitude,
            longitude: res.longitude,
            accuracy: res.accuracy
          })
        },
        fail: (error) => {
          // Platform-specific fallbacks
          // #ifdef APP-PLUS
          this.getAppLocation().then(resolve).catch(reject)
          // #endif
          
          // #ifdef H5
          this.getWebLocation().then(resolve).catch(reject)
          // #endif
          
          // #ifndef APP-PLUS || H5
          reject(error)
          // #endif
        }
      })
    })
  },
  
  // #ifdef APP-PLUS
  getAppLocation() {
    return new Promise((resolve, reject) => {
      plus.geolocation.getCurrentPosition(
        (position) => {
          resolve({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            accuracy: position.coords.accuracy
          })
        },
        reject
      )
    })
  },
  // #endif
  
  // #ifdef H5
  getWebLocation() {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        reject(new Error('Geolocation not supported'))
        return
      }
      
      navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            accuracy: position.coords.accuracy
          })
        },
        reject
      )
    })
  }
  // #endif
}

Q: How do I handle WeChat Mini Program specific APIs?

A: Use WeChat-specific APIs with proper error handling:

javascript
// WeChat Mini Program specific features
export const WeChatFeatures = {
  // WeChat login
  async wechatLogin() {
    // #ifdef MP-WEIXIN
    try {
      const loginResult = await wx.login()
      const userInfo = await wx.getUserInfo()
      
      // Send to your server for authentication
      const authResult = await uni.request({
        url: '/api/auth/wechat',
        method: 'POST',
        data: {
          code: loginResult.code,
          userInfo: userInfo
        }
      })
      
      return authResult.data
    } catch (error) {
      console.error('WeChat login failed:', error)
      throw error
    }
    // #endif
    
    // #ifndef MP-WEIXIN
    throw new Error('WeChat login only available in WeChat Mini Program')
    // #endif
  },
  
  // WeChat payment
  async wechatPay(paymentData) {
    // #ifdef MP-WEIXIN
    return new Promise((resolve, reject) => {
      wx.requestPayment({
        ...paymentData,
        success: resolve,
        fail: reject
      })
    })
    // #endif
    
    // #ifndef MP-WEIXIN
    throw new Error('WeChat payment only available in WeChat Mini Program')
    // #endif
  },
  
  // Share to WeChat
  shareToWeChat(shareData) {
    // #ifdef MP-WEIXIN
    wx.shareAppMessage({
      title: shareData.title,
      desc: shareData.description,
      path: shareData.path,
      imageUrl: shareData.imageUrl
    })
    // #endif
  }
}

Performance and Optimization

Q: How do I optimize API calls for better performance?

A: Implement caching, batching, and request optimization:

javascript
// API optimization utilities
export const APIOptimizer = {
  cache: new Map(),
  pendingRequests: new Map(),
  
  // Request with caching
  async cachedRequest(url, options = {}) {
    const cacheKey = `${url}_${JSON.stringify(options)}`
    const cacheTime = options.cacheTime || 5 * 60 * 1000 // 5 minutes
    
    // Check cache
    const cached = this.cache.get(cacheKey)
    if (cached && (Date.now() - cached.timestamp) < cacheTime) {
      return cached.data
    }
    
    // Check if request is already pending
    if (this.pendingRequests.has(cacheKey)) {
      return this.pendingRequests.get(cacheKey)
    }
    
    // Make request
    const requestPromise = uni.request({
      url,
      ...options
    }).then(response => {
      // Cache response
      this.cache.set(cacheKey, {
        data: response.data,
        timestamp: Date.now()
      })
      
      // Remove from pending
      this.pendingRequests.delete(cacheKey)
      
      return response.data
    }).catch(error => {
      // Remove from pending on error
      this.pendingRequests.delete(cacheKey)
      throw error
    })
    
    // Store pending request
    this.pendingRequests.set(cacheKey, requestPromise)
    
    return requestPromise
  },
  
  // Batch multiple requests
  async batchRequests(requests) {
    try {
      const results = await Promise.allSettled(
        requests.map(request => uni.request(request))
      )
      
      return results.map((result, index) => ({
        success: result.status === 'fulfilled',
        data: result.status === 'fulfilled' ? result.value.data : null,
        error: result.status === 'rejected' ? result.reason : null,
        originalRequest: requests[index]
      }))
    } catch (error) {
      console.error('Batch requests failed:', error)
      throw error
    }
  },
  
  // Debounced request (for search, etc.)
  debouncedRequest: (() => {
    const timeouts = new Map()
    
    return (key, requestFn, delay = 300) => {
      return new Promise((resolve, reject) => {
        // Clear existing timeout
        if (timeouts.has(key)) {
          clearTimeout(timeouts.get(key))
        }
        
        // Set new timeout
        const timeoutId = setTimeout(async () => {
          try {
            const result = await requestFn()
            resolve(result)
          } catch (error) {
            reject(error)
          } finally {
            timeouts.delete(key)
          }
        }, delay)
        
        timeouts.set(key, timeoutId)
      })
    }
  })(),
  
  // Clear cache
  clearCache(pattern) {
    if (pattern) {
      for (const key of this.cache.keys()) {
        if (key.includes(pattern)) {
          this.cache.delete(key)
        }
      }
    } else {
      this.cache.clear()
    }
  }
}

Error Handling

Q: How do I implement proper error handling for APIs?

A: Create a comprehensive error handling system:

javascript
// Error handling utilities
export const ErrorHandler = {
  // Error types
  ERROR_TYPES: {
    NETWORK: 'NETWORK_ERROR',
    TIMEOUT: 'TIMEOUT_ERROR',
    SERVER: 'SERVER_ERROR',
    CLIENT: 'CLIENT_ERROR',
    UNKNOWN: 'UNKNOWN_ERROR'
  },
  
  // Parse error from response
  parseError(error) {
    if (!error) {
      return {
        type: this.ERROR_TYPES.UNKNOWN,
        message: 'Unknown error occurred',
        code: 'UNKNOWN'
      }
    }
    
    // Network errors
    if (error.errMsg && error.errMsg.includes('network')) {
      return {
        type: this.ERROR_TYPES.NETWORK,
        message: 'Network connection failed',
        code: 'NETWORK_FAILED',
        original: error
      }
    }
    
    // Timeout errors
    if (error.errMsg && error.errMsg.includes('timeout')) {
      return {
        type: this.ERROR_TYPES.TIMEOUT,
        message: 'Request timeout',
        code: 'REQUEST_TIMEOUT',
        original: error
      }
    }
    
    // HTTP status errors
    if (error.statusCode) {
      const statusCode = error.statusCode
      
      if (statusCode >= 400 && statusCode < 500) {
        return {
          type: this.ERROR_TYPES.CLIENT,
          message: error.data?.message || `Client error (${statusCode})`,
          code: `HTTP_${statusCode}`,
          statusCode,
          original: error
        }
      }
      
      if (statusCode >= 500) {
        return {
          type: this.ERROR_TYPES.SERVER,
          message: error.data?.message || `Server error (${statusCode})`,
          code: `HTTP_${statusCode}`,
          statusCode,
          original: error
        }
      }
    }
    
    return {
      type: this.ERROR_TYPES.UNKNOWN,
      message: error.message || 'Unknown error occurred',
      code: 'UNKNOWN',
      original: error
    }
  },
  
  // Handle error with user feedback
  handleError(error, options = {}) {
    const parsedError = this.parseError(error)
    
    // Log error
    console.error('API Error:', parsedError)
    
    // Show user feedback
    if (!options.silent) {
      let message = parsedError.message
      
      // Customize message based on error type
      switch (parsedError.type) {
        case this.ERROR_TYPES.NETWORK:
          message = 'Network connection failed. Please check your internet connection.'
          break
        case this.ERROR_TYPES.TIMEOUT:
          message = 'Request timeout. Please try again.'
          break
        case this.ERROR_TYPES.SERVER:
          message = 'Server error. Please try again later.'
          break
      }
      
      uni.showToast({
        title: message,
        icon: 'none',
        duration: 3000
      })
    }
    
    // Custom error handler
    if (options.onError) {
      options.onError(parsedError)
    }
    
    return parsedError
  },
  
  // Retry mechanism
  async retryRequest(requestFn, maxRetries = 3, delay = 1000) {
    let lastError
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await requestFn()
      } catch (error) {
        lastError = error
        
        const parsedError = this.parseError(error)
        
        // Don't retry client errors
        if (parsedError.type === this.ERROR_TYPES.CLIENT) {
          throw error
        }
        
        // Wait before retry (exponential backoff)
        if (attempt < maxRetries) {
          await new Promise(resolve => 
            setTimeout(resolve, delay * Math.pow(2, attempt - 1))
          )
        }
      }
    }
    
    throw lastError
  }
}

// Usage example
try {
  const data = await ErrorHandler.retryRequest(
    () => uni.request({
      url: '/api/data',
      method: 'GET'
    }),
    3, // max retries
    1000 // initial delay
  )
  
  console.log('Success:', data)
} catch (error) {
  ErrorHandler.handleError(error, {
    onError: (parsedError) => {
      // Custom error handling
      if (parsedError.code === 'HTTP_401') {
        // Redirect to login
        uni.navigateTo({
          url: '/pages/login/login'
        })
      }
    }
  })
}

Best Practices

  1. Always handle errors gracefully
  2. Use appropriate API methods for each platform
  3. Implement caching for frequently accessed data
  4. Use request interceptors for common functionality
  5. Optimize network requests with batching and debouncing
  6. Provide meaningful user feedback for API operations
  7. Test APIs across all target platforms
  8. Implement proper retry mechanisms for network failures

Summary

Understanding uni-app APIs and their platform-specific behaviors is crucial for building robust cross-platform applications. By following these best practices and implementing proper error handling, caching, and optimization strategies, you can create reliable and performant applications that work seamlessly across all supported platforms.

一次开发,多端部署 - 让跨平台开发更简单