first commit
This commit is contained in:
531
docs/性能优化清单.md
Normal file
531
docs/性能优化清单.md
Normal file
@@ -0,0 +1,531 @@
|
||||
# 性能优化清单
|
||||
|
||||
## 一、首屏加载优化
|
||||
|
||||
### 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 调用
|
||||
- [ ] 及时清理定时器和监听器
|
||||
- [ ] 使用防抖和节流
|
||||
- [ ] 添加骨架屏
|
||||
- [ ] 实现性能监控
|
||||
Reference in New Issue
Block a user