Files
dabai_web_f/docs/性能优化清单.md
2026-03-17 14:52:07 +08:00

9.6 KiB
Raw Blame History

性能优化清单

一、首屏加载优化

1.1 代码分割

// 路由懒加载
const UserProfile = () => import('@/pages/user/profile')

// 组件懒加载
components: {
  HeavyComponent: () => import('@/components/HeavyComponent.vue')
}

1.2 小程序分包加载

{
  "subPackages": [
    {
      "root": "subPackages/order",
      "pages": ["list/index", "detail/index"]
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["subPackages/order"]
    }
  }
}

1.3 资源预加载

// 预加载关键资源
onLoad() {
  // 预加载下一页数据
  this.preloadNextPage()
  
  // 预加载图片
  uni.preloadImage({
    src: '/static/images/banner.jpg'
  })
}

1.4 骨架屏

<template>
  <view v-if="loading" class="skeleton">
    <view class="skeleton-avatar" />
    <view class="skeleton-line" />
    <view class="skeleton-line" />
  </view>
  <view v-else class="content">
    <!-- 实际内容 -->
  </view>
</template>

二、渲染性能优化

2.1 虚拟列表

对于长列表使用虚拟滚动:

<template>
  <recycle-list 
    :list="dataList"
    :item-height="100"
  >
    <template v-slot="{ item }">
      <view class="list-item">
        {{ item.title }}
      </view>
    </template>
  </recycle-list>
</template>

2.2 图片懒加载

<template>
  <!-- 小程序端 -->
  <image 
    :src="imageSrc" 
    lazy-load 
    mode="aspectFill"
  />
  
  <!-- H5端使用 Intersection Observer -->
  <img 
    v-lazy="imageSrc" 
    @load="handleImageLoad"
  />
</template>

2.3 防抖与节流

import { debounce, throttle } from '@/utils/performance'

export default {
  methods: {
    // 搜索输入防抖
    handleSearch: debounce(function(keyword) {
      this.search(keyword)
    }, 300),
    
    // 滚动事件节流
    handleScroll: throttle(function(e) {
      this.updateScrollPosition(e)
    }, 100)
  }
}

2.4 条件渲染优化

<template>
  <!-- 使用 v-show 代替 v-if频繁切换 -->
  <view v-show="isVisible">内容</view>
  
  <!-- 使用 v-if 代替 v-show不常切换 -->
  <view v-if="hasPermission">内容</view>
  
  <!-- 使用 v-once 渲染静态内容 -->
  <view v-once>{{ staticContent }}</view>
</template>

三、网络请求优化

3.1 请求合并

// 合并多个接口请求
async loadPageData() {
  const [bannerData, menuData, contentData] = await Promise.all([
    api.getBanner(),
    api.getMenu(),
    api.getContent()
  ])
  
  this.bannerList = bannerData
  this.menuList = menuData
  this.contentList = contentData
}

3.2 请求缓存

class RequestCache {
  constructor() {
    this.cache = new Map()
    this.cacheTime = 5 * 60 * 1000 // 5分钟
  }
  
  get(key) {
    const item = this.cache.get(key)
    if (!item) return null
    
    if (Date.now() - item.timestamp > this.cacheTime) {
      this.cache.delete(key)
      return null
    }
    
    return item.data
  }
  
  set(key, data) {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    })
  }
}

// 使用缓存
const cache = new RequestCache()

export const getUserInfo = async (userId) => {
  const cacheKey = `user_${userId}`
  const cached = cache.get(cacheKey)
  
  if (cached) return cached
  
  const data = await request.get(`/user/${userId}`)
  cache.set(cacheKey, data)
  
  return data
}

3.3 请求重试

async function requestWithRetry(config, retryCount = 0) {
  try {
    return await request(config)
  } catch (error) {
    if (retryCount < 3) {
      await new Promise(resolve => setTimeout(resolve, 1000))
      return requestWithRetry(config, retryCount + 1)
    }
    throw error
  }
}

3.4 请求取消

class RequestManager {
  constructor() {
    this.pendingRequests = new Map()
  }
  
  addRequest(key, requestTask) {
    // 取消之前的相同请求
    if (this.pendingRequests.has(key)) {
      this.pendingRequests.get(key).abort()
    }
    this.pendingRequests.set(key, requestTask)
  }
  
  removeRequest(key) {
    this.pendingRequests.delete(key)
  }
  
  cancelAll() {
    this.pendingRequests.forEach(task => task.abort())
    this.pendingRequests.clear()
  }
}

四、内存优化

4.1 及时清理

export default {
  data() {
    return {
      timer: null,
      observer: null
    }
  },
  
  beforeUnmount() {
    // 清理定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
    
    // 清理观察器
    if (this.observer) {
      this.observer.disconnect()
      this.observer = null
    }
    
    // 清理事件监听
    uni.offNetworkStatusChange()
  }
}

4.2 大数据处理

// 分批处理大数据
function processBigData(data, batchSize = 100) {
  const batches = []
  for (let i = 0; i < data.length; i += batchSize) {
    batches.push(data.slice(i, i + batchSize))
  }
  
  return batches.reduce((promise, batch) => {
    return promise.then(() => {
      return new Promise(resolve => {
        setTimeout(() => {
          processBatch(batch)
          resolve()
        }, 0)
      })
    })
  }, Promise.resolve())
}

4.3 WeakMap 使用

// 使用 WeakMap 避免内存泄漏
const cache = new WeakMap()

function cacheData(obj, data) {
  cache.set(obj, data)
}

function getCachedData(obj) {
  return cache.get(obj)
}

五、图片优化

5.1 图片压缩

// 上传前压缩图片
function compressImage(filePath) {
  return new Promise((resolve, reject) => {
    uni.compressImage({
      src: filePath,
      quality: 80,
      success: (res) => resolve(res.tempFilePath),
      fail: reject
    })
  })
}

5.2 图片格式选择

  • 照片:使用 JPEG 格式
  • 图标:使用 PNG 或 SVG 格式
  • 动画:使用 GIF 或 WebP 格式
  • 透明背景:使用 PNG 格式

5.3 响应式图片

<template>
  <image 
    :src="getImageUrl()" 
    mode="aspectFill"
  />
</template>

<script>
export default {
  methods: {
    getImageUrl() {
      const systemInfo = uni.getSystemInfoSync()
      const dpr = systemInfo.pixelRatio
      
      // 根据设备像素比选择图片
      if (dpr >= 3) {
        return this.image + '@3x.jpg'
      } else if (dpr >= 2) {
        return this.image + '@2x.jpg'
      }
      return this.image + '.jpg'
    }
  }
}
</script>

5.4 图片预加载

function preloadImages(urls) {
  return Promise.all(
    urls.map(url => {
      return new Promise((resolve, reject) => {
        uni.getImageInfo({
          src: url,
          success: resolve,
          fail: reject
        })
      })
    })
  )
}

六、小程序特定优化

6.1 setData 优化

// 避免频繁 setData
let updateTimer = null
let pendingData = {}

function batchUpdate(data) {
  Object.assign(pendingData, data)
  
  if (updateTimer) return
  
  updateTimer = setTimeout(() => {
    this.setData(pendingData)
    pendingData = {}
    updateTimer = null
  }, 16) // 约60fps
}

// 避免 setData 传递大数据
// 不好的做法
this.setData({
  list: this.data.list.concat(newList)
})

// 好的做法
this.setData({
  [`list[${this.data.list.length}]`]: newItem
})

6.2 自定义组件

// 使用自定义组件代替模板
Component({
  options: {
    // 启用纯数据字段
    pureDataPattern: /^_/,
    // 启用多slot支持
    multipleSlots: true
  },
  
  properties: {
    title: String
  },
  
  data: {
    _privateData: 'private' // 纯数据字段,不参与渲染
  }
})

6.3 分包预下载

{
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["subPackages/order"]
    }
  }
}

七、H5 特定优化

7.1 PWA 支持

// 注册 Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
}

// sw.js
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/static/css/main.css',
        '/static/js/main.js'
      ])
    })
  )
})

7.2 CDN 加速

// 静态资源使用 CDN
const CDN_URL = 'https://cdn.example.com'

export const getStaticUrl = (path) => {
  return `${CDN_URL}${path}`
}

7.3 Gzip 压缩

// vite.config.js
import viteCompression from 'vite-plugin-compression'

export default {
  plugins: [
    viteCompression({
      algorithm: 'gzip',
      ext: '.gz'
    })
  ]
}

八、性能监控

8.1 性能指标收集

// 页面加载时间
const pageLoadTime = performance.timing.loadEventEnd - performance.timing.navigationStart

// 首屏渲染时间
const firstPaintTime = performance.getEntriesByType('paint')[0].startTime

// 接口请求时间
const apiTime = performance.getEntriesByType('resource')
  .filter(item => item.initiatorType === 'xmlhttprequest')

8.2 性能上报

function reportPerformance(data) {
  // 上报到监控平台
  uni.request({
    url: 'https://monitor.example.com/report',
    method: 'POST',
    data: {
      type: 'performance',
      ...data
    }
  })
}

九、优化检查清单

  • 启用代码分割和懒加载
  • 实现图片懒加载
  • 使用虚拟列表处理长列表
  • 添加请求缓存机制
  • 实现请求重试机制
  • 合并并发请求
  • 压缩图片资源
  • 使用 CDN 加速静态资源
  • 启用 Gzip 压缩
  • 小程序分包加载
  • 优化 setData 调用
  • 及时清理定时器和监听器
  • 使用防抖和节流
  • 添加骨架屏
  • 实现性能监控