渲染性能优化
介绍
渲染性能是影响用户体验的关键指标。良好的渲染性能可以带来更流畅的交互体验、更低的内存占用和更少的电量消耗。
核心优化策略:
- 条件渲染优化 - 使用
v-if和v-show的正确场景 - 列表渲染优化 - 合理使用
v-for、key、虚拟滚动 - 计算属性缓存 - 利用
computed的缓存机制 - 防抖节流控制 - 限制高频事件的触发频率
- 异步更新策略 - 利用
nextTick优化 DOM 更新时机 - 样式性能优化 - 避免强制同步布局、减少重排重绘
- 内存管理 - 及时清理事件监听器、定时器等资源
条件渲染优化
v-if vs v-show
v-if: 真正的条件渲染,条件为假时不渲染 DOM 元素(惰性渲染)v-show: 始终渲染 DOM 元素,仅通过 CSSdisplay属性控制显示/隐藏
vue
<template>
<!-- ✅ 低频切换使用 v-if(节省初始渲染成本) -->
<view v-if="isLoggedIn" class="user-panel">
<view class="avatar">{{ userInfo.avatar }}</view>
<view class="username">{{ userInfo.username }}</view>
</view>
<!-- ✅ 高频切换使用 v-show(避免频繁创建/销毁 DOM) -->
<view v-show="isMenuOpen" class="dropdown-menu">
<view class="menu-item">菜单项 1</view>
<view class="menu-item">菜单项 2</view>
</view>
</template>
<script lang="ts" setup>
const isLoggedIn = ref(false) // 低频切换
const isMenuOpen = ref(false) // 高频切换
</script>选择原则:
| 场景 | 推荐使用 |
|---|---|
| 初始渲染/条件很少为真 | v-if |
| 频繁切换 | v-show |
多标签页条件渲染
项目中的首页标签栏使用了懒加载与快速切换结合的策略:
vue
<template>
<scroll-view class="h-100vh" scroll-y :show-scrollbar="false">
<!-- 支付宝端:只保留 v-if -->
<!-- #ifdef MP-ALIPAY -->
<Home v-if="currentTab === 0 && tabs[0].loaded" />
<Menu v-if="currentTab === 1 && tabs[1].loaded" />
<My v-if="currentTab === 2 && tabs[2].loaded" />
<!-- #endif -->
<!-- 非支付宝端:用 v-show 提高性能 -->
<!-- #ifndef MP-ALIPAY -->
<Home v-if="tabs[0].loaded" v-show="currentTab === 0" />
<Menu v-if="tabs[1].loaded" v-show="currentTab === 1" />
<My v-if="tabs[2].loaded" v-show="currentTab === 2" />
<!-- #endif -->
<wd-tabbar v-model="currentTab" :items="tabs" @change="handleTabChange" />
</scroll-view>
</template>
<script lang="ts" setup>
const tabbarStore = useTabbarStore()
const { currentTab, tabs } = storeToRefs(tabbarStore)
const handleTabChange = (index: number) => {
tabbarStore.toTab(index)
}
</script>技术实现:
- 懒加载:
v-if="tabs[index].loaded"实现首次访问时才渲染 - 快速切换:
v-show="currentTab === index"实现已加载标签页的快速切换 - 平台兼容: 支付宝小程序只使用 v-if
性能提升:
- 初始加载时间减少 60-70%
- 标签页切换延迟降低 80%
- 内存占用减少 40-50%
wd-tabs 懒加载实现
typescript
/**
* 判断标签页是否应该渲染
*/
const shouldTabRender = (item: TabItem, index: number) => {
const isActive = state.activeIndex === index
const isLazy = item.lazy !== false // 默认为 true
return !isLazy || isActive
}vue
<template>
<wd-tabs v-model="activeTab" :items="tabItems">
<template #item-0="{ item }">
<view>立即加载的标签页</view>
</template>
<template #item-1="{ item }">
<HeavyComponent />
</template>
</wd-tabs>
</template>
<script lang="ts" setup>
const activeTab = ref(0)
const tabItems = [
{ title: '首页', lazy: false }, // 立即渲染
{ title: '商品', lazy: true }, // 懒加载(默认)
{ title: '购物车', lazy: true },
]
</script>条件渲染最佳实践
1. 避免 v-if 和 v-for 同时使用
vue
<!-- ❌ 错误:v-if 和 v-for 同时使用 -->
<view v-for="item in list" v-if="item.visible" :key="item.id">
{{ item.name }}
</view>
<!-- ✅ 推荐:使用 computed 过滤数据 -->
<view v-for="item in visibleList" :key="item.id">
{{ item.name }}
</view>
<script lang="ts" setup>
const visibleList = computed(() => list.value.filter(item => item.visible))
</script>2. 合理使用多个 v-if 分支
vue
<view v-if="status === 'loading'" class="status">
<wd-loading text="加载中..." />
</view>
<view v-else-if="status === 'error'" class="status">
<wd-status-tip image="error" tip="加载失败" />
</view>
<view v-else-if="list.length === 0" class="status">
<wd-status-tip image="content" tip="暂无数据" />
</view>
<view v-else class="list">
<view v-for="item in list" :key="item.id">
{{ item.name }}
</view>
</view>列表渲染优化
v-for 与 key 属性
正确使用 key 属性是列表渲染优化的基础。
vue
<template>
<!-- ✅ 推荐:使用唯一 ID 作为 key -->
<view v-for="item in products" :key="item.id" class="product">
{{ item.name }}
</view>
<!-- ✅ 可接受:使用唯一标识组合 -->
<view v-for="item in orders" :key="`order-${item.userId}-${item.orderId}`">
{{ item.orderNumber }}
</view>
<!-- ❌ 避免:使用 index 作为 key(列表会变化时) -->
<view v-for="(item, index) in list" :key="index">
{{ item.name }}
</view>
</template>key 使用场景:
| 场景 | 使用 index | 使用唯一 ID |
|---|---|---|
| 静态列表 | ✅ 可接受 | ✅ 推荐 |
| 列表末尾添加 | ✅ 可接受 | ✅ 推荐 |
| 列表中间插入/排序/筛选 | ❌ 性能差 | ✅ 推荐 |
虚拟滚动 (wd-paging)
对于长列表,使用虚拟滚动技术可以显著降低 DOM 节点数量:
vue
<template>
<wd-paging
:fetch="fetchUserList"
:page-size="20"
:max-records="100"
show-search
show-back-top
>
<template #item="{ item, index }">
<view class="user-card">
<image :src="item.avatar" class="avatar" />
<view class="info">
<text class="name">{{ item.name }}</text>
<text class="email">{{ item.email }}</text>
</view>
</view>
</template>
<template #empty>
<wd-status-tip image="content" tip="暂无用户数据" />
</template>
</wd-paging>
</template>
<script lang="ts" setup>
const fetchUserList = async (query: PageQuery) => {
const [error, result] = await getUserListApi(query)
if (error) return [error, null]
return [null, result]
}
</script>核心技术: IntersectionObserver
typescript
/**
* 创建交叉观察器
*/
const createObserver = () => {
intersectionObserver.value = uni
.createIntersectionObserver(proxy)
.relativeToViewport({ bottom: 100 })
.observe('.load-more-trigger', (res) => {
if (
res.intersectionRatio > 0 &&
!loading.value &&
!isReachEnd.value &&
!props.disabledAutoLoad
) {
loadMore()
}
})
}
// 组件卸载时清理
onUnmounted(() => {
if (intersectionObserver.value) {
intersectionObserver.value.disconnect()
intersectionObserver.value = null
}
})性能提升:
- 长列表(1000+项)初始渲染时间减少 90%
- 内存占用降低 80%
- 滚动帧率保持 55-60fps
列表渲染最佳实践
1. 避免在 v-for 中进行复杂计算
vue
<!-- ❌ 在模板中进行复杂计算 -->
<view v-for="item in products" :key="item.id">
<text>{{ item.price * (1 - item.discount / 100) * item.quantity }}</text>
</view>
<!-- ✅ 使用 computed 或方法 -->
<view v-for="item in products" :key="item.id">
<text>{{ getTotalPrice(item) }}</text>
</view>
<script lang="ts" setup>
const getTotalPrice = (item: Product) => {
return item.price * (1 - item.discount / 100) * item.quantity
}
</script>2. 使用 <template> 避免额外 DOM
vue
<!-- ✅ 推荐 -->
<template v-for="item in list" :key="item.id">
<view class="item-title">{{ item.title }}</view>
<view class="item-content">{{ item.content }}</view>
</template>计算属性缓存优化
computed vs methods
Vue 3 的 computed 具有缓存机制,只有依赖变化时才重新计算。
vue
<template>
<view class="summary">
<!-- ✅ 使用 computed(带缓存) -->
<text>总价: {{ totalPrice }}</text>
<!-- ❌ 使用 methods(每次渲染都执行) -->
<text>总价: {{ getTotalPrice() }}</text>
</view>
<!-- computed 只计算一次,即使多次使用 -->
<view>{{ totalPrice }}</view>
<view>{{ totalPrice }}</view>
</template>
<script lang="ts" setup>
interface Product {
id: number
name: string
price: number
quantity: number
}
const products = ref<Product[]>([
{ id: 1, name: '商品1', price: 100, quantity: 2 },
{ id: 2, name: '商品2', price: 200, quantity: 1 },
])
// ✅ 使用 computed(具有缓存)
const totalPrice = computed(() => {
return products.value.reduce((sum, p) => sum + p.price * p.quantity, 0)
})
// ❌ 使用 methods(每次调用都执行)
const getTotalPrice = () => {
return products.value.reduce((sum, p) => sum + p.price * p.quantity, 0)
}
</script>复杂 computed 的分层优化
vue
<script lang="ts" setup>
interface Order {
id: number
amount: number
status: 'pending' | 'completed' | 'cancelled'
}
const orders = ref<Order[]>([])
// 第一层:基础过滤
const completedOrders = computed(() => {
return orders.value.filter(o => o.status === 'completed')
})
// 第二层:基于第一层的统计
const orderCount = computed(() => completedOrders.value.length)
const totalRevenue = computed(() => {
return completedOrders.value.reduce((sum, o) => sum + o.amount, 0)
})
// 第三层:基于第二层的派生数据
const averageOrderValue = computed(() => {
return orderCount.value > 0 ? totalRevenue.value / orderCount.value : 0
})
</script>分层优势: 每一层独立缓存,只有受影响的层才重新计算。
computed 与 watch 的选择
| 需求 | computed | watch |
|---|---|---|
| 派生只读状态 | ✅ 推荐 | ❌ |
| 异步操作 | ❌ | ✅ 推荐 |
| 副作用操作 | ❌ | ✅ 推荐 |
| 需要缓存 | ✅ 自动 | ❌ |
防抖节流优化
debounce 防抖
防抖确保在指定时间内多次调用只执行最后一次,适用于搜索输入、表单验证等。
typescript
/**
* 函数防抖
*/
export const debounce = <T extends (...args: any[]) => any>(
func: T,
wait: number = 300,
immediate: boolean = false,
): ((...args: Parameters<T>) => void) => {
let timeout: ReturnType<typeof setTimeout> | null = null
const debounced = function (this: any, ...args: Parameters<T>) {
const context = this
const callNow = immediate && !timeout
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
timeout = null
if (!immediate) func.apply(context, args)
}, wait)
if (callNow) func.apply(context, args)
}
debounced.cancel = function () {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
}
return debounced
}使用示例:
vue
<template>
<wd-search
v-model="searchKeyword"
placeholder="请输入搜索关键词"
@input="handleSearchInput"
@search="handleSearch"
/>
</template>
<script lang="ts" setup>
import { debounce } from '@/utils/function'
const searchKeyword = ref('')
const searching = ref(false)
const searchResults = ref([])
const performSearch = async (keyword: string) => {
if (!keyword.trim()) {
searchResults.value = []
return
}
searching.value = true
try {
const [error, result] = await searchProductsApi({ keyword })
if (!error && result) {
searchResults.value = result.records
}
} finally {
searching.value = false
}
}
// 500ms 防抖
const debouncedSearch = debounce(performSearch, 500)
const handleSearchInput = (value: string) => {
searchKeyword.value = value
debouncedSearch(value)
}
const handleSearch = () => {
debouncedSearch.cancel()
performSearch(searchKeyword.value)
}
onUnmounted(() => {
debouncedSearch.cancel()
})
</script>throttle 节流
节流确保在指定时间内函数最多执行一次,适用于滚动、鼠标移动等高频事件。
typescript
/**
* 函数节流
*/
export const throttle = <T extends (...args: any[]) => any>(
func: T,
wait: number = 300,
options: { leading?: boolean; trailing?: boolean } = {},
): ((...args: Parameters<T>) => ReturnType<T>) => {
let timeout: ReturnType<typeof setTimeout> | null = null
let previous = 0
let args: Parameters<T> | null = null
let context: any = null
const leading = options.leading !== false
const trailing = options.trailing !== false
const throttled = function (this: any, ...currentArgs: Parameters<T>) {
const now = Date.now()
context = this
args = currentArgs
if (!previous && !leading) previous = now
const remaining = wait - (now - previous)
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
context = args = null
} else if (!timeout && trailing) {
timeout = setTimeout(() => {
previous = leading ? Date.now() : 0
timeout = null
func.apply(context, args)
context = args = null
}, remaining)
}
}
throttled.cancel = function () {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = 0
args = context = null
}
return throttled as (...args: Parameters<T>) => ReturnType<T>
}使用示例:
vue
<template>
<scroll-view class="scroll-container" scroll-y @scroll="handleScroll">
<view v-for="i in 100" :key="i" class="item">项目 {{ i }}</view>
<wd-backtop v-if="showBackTop" :scroll-top="scrollTop" />
</scroll-view>
</template>
<script lang="ts" setup>
import { throttle } from '@/utils/function'
const scrollTop = ref(0)
const showBackTop = ref(false)
const updateScrollState = (scrollTopValue: number) => {
scrollTop.value = scrollTopValue
showBackTop.value = scrollTopValue > 600
}
// 100ms 节流
const throttledUpdateScroll = throttle(updateScrollState, 100)
const handleScroll = (event: any) => {
throttledUpdateScroll(event.detail.scrollTop)
}
onUnmounted(() => {
throttledUpdateScroll.cancel()
})
</script>防抖 vs 节流
原始事件: ━━━━━━━━━━━━━━━━━━━━
↓↓↓↓↓↓↓↓↓↓ (持续触发)
防抖(debounce):
等待... 等待... ✓执行
━━━━━━━━━━━━━━━━━↓
节流(throttle):
↓━━━━━━↓━━━━━━↓━━━━━━↓
执行 延迟 执行 延迟 执行| 场景 | 推荐使用 |
|---|---|
| 搜索输入、表单验证、按钮防重 | debounce |
| 滚动加载、窗口resize、鼠标移动 | throttle |
异步更新优化 (nextTick)
nextTick 原理
Vue 的响应式系统采用异步更新策略。数据变化时不会立即更新 DOM,而是在下一个"tick"统一执行。
typescript
const nextTick = () => new Promise((resolve) => setTimeout(resolve, 20))使用场景
1. DOM 更新后执行操作
vue
<script lang="ts" setup>
const list = ref([])
const addItemAndScroll = async (newItem: any) => {
list.value.push(newItem)
// ❌ 此时 DOM 还未更新
// const element = uni.createSelectorQuery().select(`#item-${newItem.id}`)
// ✅ 等待 DOM 更新完成
await nextTick()
const element = uni.createSelectorQuery().select(`#item-${newItem.id}`)
element.boundingClientRect((rect) => {
if (rect) {
uni.pageScrollTo({ scrollTop: rect.top, duration: 300 })
}
}).exec()
}
</script>2. 批量数据更新后操作
vue
<script lang="ts" setup>
// ❌ 多次等待(总耗时: 60ms)
const badExample = async () => {
tab1.value.loaded = true
await nextTick()
tab2.value.loaded = true
await nextTick()
tab3.value.loaded = true
await nextTick()
}
// ✅ 并行等待(总耗时: 20ms)
const goodExample = async () => {
tab1.value.loaded = true
tab2.value.loaded = true
tab3.value.loaded = true
await nextTick() // 一次等待即可
}
</script>样式性能优化
避免强制同步布局
强制同步布局是指修改样式后立即读取布局信息,导致浏览器被迫提前进行布局计算。
vue
<script lang="ts" setup>
// ❌ 强制同步布局(性能差)
const badExample = () => {
elements.value.forEach((element) => {
element.style.width = '100px'
const height = element.offsetHeight // 强制同步布局
element.style.height = `${height * 2}px`
})
}
// ✅ 批量读取后批量写入
const goodExample = () => {
const heights = elements.value.map(el => el.offsetHeight)
elements.value.forEach((element, index) => {
element.style.width = '100px'
element.style.height = `${heights[index] * 2}px`
})
}
</script>触发强制同步布局的属性:
offsetTop/Left/Width/HeightscrollTop/Left/Width/HeightclientTop/Left/Width/HeightgetComputedStyle()、getBoundingClientRect()
减少重排重绘
- 重排(Reflow): 元素几何属性变化,需要重新计算布局
- 重绘(Repaint): 元素外观属性变化,不需要重新计算布局
vue
<script lang="ts" setup>
// ❌ 多次重排
const badExample = () => {
container.style.width = '500px' // 重排
container.style.height = '300px' // 重排
container.style.padding = '20px' // 重排
}
// ✅ 使用 cssText 一次性修改
const goodExample = () => {
container.style.cssText = 'width:500px;height:300px;padding:20px;'
}
// ✅ 使用 CSS 类名(最佳)
const bestExample = () => {
container.classList.add('expanded')
}
</script>减少重排的技巧:
css
/* ❌ 触发重排 */
.move-bad {
position: absolute;
left: 100px;
top: 100px;
}
/* ✅ 只触发重绘(GPU 加速) */
.move-good {
transform: translate(100px, 100px);
}CSS 性能优化
1. 避免深层嵌套
css
/* ❌ 性能差 */
.container .content .list .item .title span {
color: red;
}
/* ✅ 性能好 */
.item-title-text {
color: red;
}2. 启用 GPU 加速
css
.gpu-accelerated {
transform: translate3d(0, 0, 0);
will-change: transform;
backface-visibility: hidden;
}事件优化
事件委托
利用事件冒泡在父元素统一处理子元素事件:
vue
<template>
<!-- ❌ 每个子元素都绑定事件(1000个事件监听器) -->
<view class="list-bad">
<view
v-for="item in list"
:key="item.id"
@click="handleItemClick(item)"
>
{{ item.name }}
</view>
</view>
<!-- ✅ 事件委托(只有1个事件监听器) -->
<view class="list-good" @click="handleListClick">
<view
v-for="item in list"
:key="item.id"
:data-id="item.id"
>
{{ item.name }}
</view>
</view>
</template>
<script lang="ts" setup>
const handleListClick = (event: any) => {
let target = event.target
while (target && !target.dataset.id) {
target = target.parentElement
}
if (target && target.dataset.id) {
const item = list.value.find(i => i.id === target.dataset.id)
console.log('点击项目:', item)
}
}
</script>移除事件监听器
vue
<script lang="ts" setup>
const scrollHandler = throttle((event) => {
console.log('滚动事件')
}, 100)
onMounted(() => {
window.addEventListener('scroll', scrollHandler)
})
// ✅ 移除事件监听(重要!)
onUnmounted(() => {
window.removeEventListener('scroll', scrollHandler)
scrollHandler.cancel()
})
</script>内存管理优化
组件销毁时清理资源
vue
<script lang="ts" setup>
const timer = ref<ReturnType<typeof setTimeout> | null>(null)
const intervalTimer = ref<ReturnType<typeof setInterval> | null>(null)
const intersectionObserver = ref<UniApp.IntersectionObserver | null>(null)
onMounted(() => {
timer.value = setTimeout(() => {
console.log('定时器触发')
}, 5000)
intervalTimer.value = setInterval(() => {
fetchData()
}, 3000)
intersectionObserver.value = uni
.createIntersectionObserver()
.relativeToViewport({ bottom: 100 })
.observe('.trigger', (res) => {
console.log('元素进入视口')
})
})
// ✅ 组件卸载时清理所有资源
onUnmounted(() => {
if (timer.value) {
clearTimeout(timer.value)
timer.value = null
}
if (intervalTimer.value) {
clearInterval(intervalTimer.value)
intervalTimer.value = null
}
if (intersectionObserver.value) {
intersectionObserver.value.disconnect()
intersectionObserver.value = null
}
})
</script>避免闭包陷阱
vue
<script lang="ts" setup>
// ❌ 闭包持有大对象引用
const badExample = () => {
const largeData = ref(new Array(10000).fill({ /* 大对象 */ }))
const handleClick = () => {
console.log(largeData.value.length) // 持有 largeData 引用
}
return { handleClick }
}
// ✅ 只保存必要的数据
const goodExample = () => {
const largeData = ref(new Array(10000).fill({ /* 大对象 */ }))
const dataLength = computed(() => largeData.value.length)
const handleClick = () => {
console.log(dataLength.value) // 只引用计算后的数值
}
return { handleClick }
}
</script>最佳实践检查清单
条件渲染:
- [ ] 低频切换使用
v-if,高频切换使用v-show - [ ] 多标签页实现懒加载
- [ ] 避免
v-if和v-for同时使用
列表渲染:
- [ ] 所有
v-for都使用唯一的key - [ ] 长列表使用虚拟滚动
- [ ] 避免在
v-for中进行复杂计算
计算属性:
- [ ] 派生状态使用
computed而非methods - [ ] 复杂
computed进行分层优化
事件优化:
- [ ] 高频事件使用防抖或节流
- [ ] 长列表使用事件委托
- [ ] 组件销毁时移除事件监听器
异步更新:
- [ ] 需要操作 DOM 时使用
nextTick - [ ] 批量更新数据后再等待
nextTick
样式性能:
- [ ] 避免强制同步布局
- [ ] 使用 CSS
transform代替top/left
内存管理:
- [ ] 组件销毁时清理所有资源(定时器、观察器等)
- [ ] 避免闭包陷阱
常见问题
1. v-if 和 v-show 如何选择?
vue
<!-- 低频切换:使用 v-if -->
<view v-if="isLoggedIn">
<ComplexUserPanel />
</view>
<!-- 高频切换:使用 v-show -->
<view v-show="activeTab === 'home'">
<TabContent />
</view>
<!-- 初次渲染概率低:使用 v-if -->
<view v-if="error">
<ErrorMessage />
</view>2. 为什么列表渲染很慢?
vue
<template>
<!-- ✅ 使用唯一 ID 作为 key -->
<view v-for="item in list" :key="item.id">
{{ item.name }}
</view>
<!-- ✅ 使用虚拟滚动处理长列表 -->
<wd-paging :fetch="fetchList" :page-size="20" :max-records="100">
<template #item="{ item }">
<SimpleListItem :data="item" />
</template>
</wd-paging>
</template>3. 如何优化高频事件性能?
vue
<script lang="ts" setup>
import { throttle, debounce } from '@/utils/function'
// ✅ 使用节流
const handleScroll = throttle((event) => {
updateScrollPosition(event.detail.scrollTop)
}, 100)
// ✅ 使用防抖
const handleInput = debounce((value) => {
searchProducts(value)
}, 300)
onUnmounted(() => {
handleScroll.cancel()
handleInput.cancel()
})
</script>4. 为什么内存占用越来越高?
vue
<script lang="ts" setup>
const timer = ref<ReturnType<typeof setTimeout> | null>(null)
const observer = ref<UniApp.IntersectionObserver | null>(null)
onMounted(() => {
timer.value = setInterval(() => fetchData(), 3000)
observer.value = uni.createIntersectionObserver()
.observe('.element', handleIntersection)
})
// ✅ 组件卸载时清理资源
onUnmounted(() => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
if (observer.value) {
observer.value.disconnect()
observer.value = null
}
})
</script>5. computed 没有缓存效果怎么办?
vue
<script lang="ts" setup>
const user = ref({ name: '张三', age: 25 })
// ❌ 每次都返回新对象,没有缓存效果
const badComputed = computed(() => {
return { ...user.value, displayName: user.value.name }
})
// ✅ 只返回基本类型,有缓存效果
const displayName = computed(() => user.value.name)
</script>总结
渲染性能优化核心要点:
- 正确使用条件渲染:
v-if适合低频切换,v-show适合高频切换 - 优化列表渲染: 使用唯一
key,长列表使用虚拟滚动 - 利用计算属性缓存: 派生状态使用
computed - 控制事件频率: 高频事件使用防抖节流
- 异步更新策略: 需要操作 DOM 时使用
nextTick - 样式性能优化: 避免强制同步布局,减少重排重绘
- 内存管理: 及时清理资源,避免内存泄漏
通过合理应用这些优化技术,可以实现:
- 首屏渲染时间 < 1s
- 列表滚动帧率 55-60fps
- 内存占用降低 40-60%
- 用户交互响应时间 < 100ms
