532 lines
9.6 KiB
Markdown
532 lines
9.6 KiB
Markdown
# 性能优化清单
|
||
|
||
## 一、首屏加载优化
|
||
|
||
### 1.1 代码分割
|
||
```javascript
|
||
// 路由懒加载
|
||
const UserProfile = () => import('@/pages/user/profile')
|
||
|
||
// 组件懒加载
|
||
components: {
|
||
HeavyComponent: () => import('@/components/HeavyComponent.vue')
|
||
}
|
||
```
|
||
|
||
### 1.2 小程序分包加载
|
||
```json
|
||
{
|
||
"subPackages": [
|
||
{
|
||
"root": "subPackages/order",
|
||
"pages": ["list/index", "detail/index"]
|
||
}
|
||
],
|
||
"preloadRule": {
|
||
"pages/index/index": {
|
||
"network": "all",
|
||
"packages": ["subPackages/order"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 1.3 资源预加载
|
||
```javascript
|
||
// 预加载关键资源
|
||
onLoad() {
|
||
// 预加载下一页数据
|
||
this.preloadNextPage()
|
||
|
||
// 预加载图片
|
||
uni.preloadImage({
|
||
src: '/static/images/banner.jpg'
|
||
})
|
||
}
|
||
```
|
||
|
||
### 1.4 骨架屏
|
||
```vue
|
||
<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 虚拟列表
|
||
对于长列表使用虚拟滚动:
|
||
|
||
```vue
|
||
<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 图片懒加载
|
||
```vue
|
||
<template>
|
||
<!-- 小程序端 -->
|
||
<image
|
||
:src="imageSrc"
|
||
lazy-load
|
||
mode="aspectFill"
|
||
/>
|
||
|
||
<!-- H5端使用 Intersection Observer -->
|
||
<img
|
||
v-lazy="imageSrc"
|
||
@load="handleImageLoad"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
### 2.3 防抖与节流
|
||
```javascript
|
||
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 条件渲染优化
|
||
```vue
|
||
<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 请求合并
|
||
```javascript
|
||
// 合并多个接口请求
|
||
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 请求缓存
|
||
```javascript
|
||
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 请求重试
|
||
```javascript
|
||
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 请求取消
|
||
```javascript
|
||
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 及时清理
|
||
```javascript
|
||
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 大数据处理
|
||
```javascript
|
||
// 分批处理大数据
|
||
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 使用
|
||
```javascript
|
||
// 使用 WeakMap 避免内存泄漏
|
||
const cache = new WeakMap()
|
||
|
||
function cacheData(obj, data) {
|
||
cache.set(obj, data)
|
||
}
|
||
|
||
function getCachedData(obj) {
|
||
return cache.get(obj)
|
||
}
|
||
```
|
||
|
||
## 五、图片优化
|
||
|
||
### 5.1 图片压缩
|
||
```javascript
|
||
// 上传前压缩图片
|
||
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 响应式图片
|
||
```vue
|
||
<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 图片预加载
|
||
```javascript
|
||
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 优化
|
||
```javascript
|
||
// 避免频繁 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 自定义组件
|
||
```javascript
|
||
// 使用自定义组件代替模板
|
||
Component({
|
||
options: {
|
||
// 启用纯数据字段
|
||
pureDataPattern: /^_/,
|
||
// 启用多slot支持
|
||
multipleSlots: true
|
||
},
|
||
|
||
properties: {
|
||
title: String
|
||
},
|
||
|
||
data: {
|
||
_privateData: 'private' // 纯数据字段,不参与渲染
|
||
}
|
||
})
|
||
```
|
||
|
||
### 6.3 分包预下载
|
||
```json
|
||
{
|
||
"preloadRule": {
|
||
"pages/index/index": {
|
||
"network": "all",
|
||
"packages": ["subPackages/order"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 七、H5 特定优化
|
||
|
||
### 7.1 PWA 支持
|
||
```javascript
|
||
// 注册 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 加速
|
||
```javascript
|
||
// 静态资源使用 CDN
|
||
const CDN_URL = 'https://cdn.example.com'
|
||
|
||
export const getStaticUrl = (path) => {
|
||
return `${CDN_URL}${path}`
|
||
}
|
||
```
|
||
|
||
### 7.3 Gzip 压缩
|
||
```javascript
|
||
// vite.config.js
|
||
import viteCompression from 'vite-plugin-compression'
|
||
|
||
export default {
|
||
plugins: [
|
||
viteCompression({
|
||
algorithm: 'gzip',
|
||
ext: '.gz'
|
||
})
|
||
]
|
||
}
|
||
```
|
||
|
||
## 八、性能监控
|
||
|
||
### 8.1 性能指标收集
|
||
```javascript
|
||
// 页面加载时间
|
||
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 性能上报
|
||
```javascript
|
||
function reportPerformance(data) {
|
||
// 上报到监控平台
|
||
uni.request({
|
||
url: 'https://monitor.example.com/report',
|
||
method: 'POST',
|
||
data: {
|
||
type: 'performance',
|
||
...data
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
## 九、优化检查清单
|
||
|
||
- [ ] 启用代码分割和懒加载
|
||
- [ ] 实现图片懒加载
|
||
- [ ] 使用虚拟列表处理长列表
|
||
- [ ] 添加请求缓存机制
|
||
- [ ] 实现请求重试机制
|
||
- [ ] 合并并发请求
|
||
- [ ] 压缩图片资源
|
||
- [ ] 使用 CDN 加速静态资源
|
||
- [ ] 启用 Gzip 压缩
|
||
- [ ] 小程序分包加载
|
||
- [ ] 优化 setData 调用
|
||
- [ ] 及时清理定时器和监听器
|
||
- [ ] 使用防抖和节流
|
||
- [ ] 添加骨架屏
|
||
- [ ] 实现性能监控
|