Best practices for responsive performance
The reactive system is one of the core features of Vue.js, but improper usage can lead to performance issues. Properly leveraging the reactive mechanism and reducing unnecessary computations and rendering can significantly improve application smoothness.
Reactive Data Optimization
Avoid declaring unnecessary reactive properties in data
. Vue recursively processes all properties in data
for reactivity, and large objects or non-reactive data can increase initialization overhead.
// Not recommended
data() {
return {
largeArray: new Array(10000).fill({ value: 0 }), // Each element will become reactive
config: Object.freeze({ // Frozen objects will still be recursively processed for reactivity
apiUrl: 'https://api.example.com',
timeout: 5000
})
}
}
// Recommended approach
data() {
return {
// Data that needs reactivity
activeItem: null,
// Non-reactive large data
largeArray: markRaw(new Array(10000).fill({ value: 0 })),
config: { // Completely avoid reactivity with Object.freeze + markRaw
apiUrl: 'https://api.example.com',
timeout: 5000
}
}
}
For complex data structures, use shallowRef
or shallowReactive
to avoid deep reactivity:
import { shallowReactive } from 'vue'
const state = shallowReactive({
nested: {
// Only the first level is reactive
data: { /* Large data structure */ }
}
})
Computed Property Caching Strategy
Computed properties cache results by default but recalculate when dependencies change. Avoid expensive operations in computed properties:
// Inefficient implementation
computed: {
expensiveCalculation() {
// Performs O(n^2) operation on every access
return this.items.reduce((acc, item) => {
return acc + this.otherArray.find(o => o.id === item.id).value
}, 0)
}
}
// Optimized solution
computed: {
itemMap() {
// First create a lookup table
return this.otherArray.reduce((map, item) => {
map[item.id] = item.value
return map
}, {})
},
expensiveCalculation() {
// Reduces complexity to O(n)
return this.items.reduce((acc, item) => {
return acc + (this.itemMap[item.id] || 0)
}, 0)
}
}
For rarely changing but frequently accessed data, manually control caching:
import { computed } from 'vue'
let cache = null
let lastDeps = null
function cachedComputed(getter) {
return computed(() => {
const newDeps = getter()
if (!shallowEqual(newDeps, lastDeps)) {
cache = expensiveOperation(newDeps)
lastDeps = newDeps
}
return cache
})
}
List Rendering Performance
When rendering large lists with v-for
, always provide a key
and use stable identifiers. Avoid using array indices as keys:
<!-- Inefficient approach -->
<ul>
<li v-for="(item, index) in items" :key="index">
{{ item.text }}
</li>
</ul>
<!-- Optimized solution -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
For very long lists (1000+ items), use virtual scrolling:
import { VirtualScroller } from 'vue-virtual-scroller'
export default {
components: { VirtualScroller },
template: `
<VirtualScroller
:items="largeList"
:item-size="50"
key-field="id"
>
<template #default="{ item }">
<div class="item">{{ item.content }}</div>
</template>
</VirtualScroller>
`
}
Watcher Optimization
Avoid deep watching large objects; explicitly specify the property paths to watch:
// Not recommended
watch: {
bigObject: {
handler() { /* Logic */ },
deep: true // Performance killer
}
}
// Recommended approach
watch: {
'bigObject.importantProp'(newVal) {
// Only watch specific properties
}
}
For high-frequency events (e.g., scrolling, mouse movement), use debouncing or throttling:
import { throttle } from 'lodash-es'
export default {
methods: {
handleScroll: throttle(function() {
// Logic
}, 100)
},
created() {
window.addEventListener('scroll', this.handleScroll)
},
beforeUnmount() {
window.removeEventListener('scroll', this.handleScroll)
}
}
Component Optimization Strategies
Properly splitting components can narrow update scopes. Use v-once
to mark static content:
<template>
<div>
<header v-once>
<!-- Header that never updates -->
<h1>{{ title }}</h1>
</header>
<main>
<!-- Dynamic content -->
</main>
</div>
</template>
For purely presentational components, use the functional
option or the Composition API's setup
function:
// Functional component
export default {
functional: true,
props: ['item'],
render(ctx) {
return h('div', ctx.item.text)
}
}
// Composition API optimization
import { h } from 'vue'
export default {
props: ['item'],
setup(props) {
return () => h('div', props.item.text)
}
}
Reactive Update Batching
Vue batches synchronous reactive updates by default. For asynchronous operations, manually control update timing:
import { nextTick } from 'vue'
async function updateMultiple() {
// These updates will be batched
state.a = 1
state.b = 2
state.c = 3
await nextTick()
// DOM updates are complete
}
In extreme performance-sensitive scenarios, use flush: 'post'
to ensure watchers execute after rendering:
watch(
() => state.someValue,
() => {
// Execute after DOM updates
},
{ flush: 'post' }
)
Memory Management
Clean up unused reactive data promptly, especially global state:
import { effectScope } from 'vue'
const scope = effectScope()
scope.run(() => {
const state = reactive({ /* ... */ })
// Related computed properties/watchers
})
// When the component unmounts
scope.stop() // Automatically cleans up all reactive effects
Avoid holding reactive references in closures long-term:
// Potential memory leak
let cachedState
export function useLeakyState() {
cachedState = reactive({ data: null })
return cachedState
}
// Safe usage
export function useSafeState() {
const state = reactive({ data: null })
onUnmounted(() => {
// Cleanup
})
return state
}
Compile-Time Optimizations
Leverage Vue 3's compile-time optimizations. When static content in templates exceeds a certain ratio, Vue automatically hoists static nodes:
// Static content in templates will be hoisted
<template>
<div>
<h1>Static Title</h1> <!-- Hoisted -->
<p>{{ dynamicContent }}</p>
</div>
</template>
Manually mark props that won't change:
<MyComponent
:static-prop="42"
v-bind="{
'static-attr': 'value',
[dynamicAttr]: dynamicValue
}"
/>
Performance Monitoring Tools
Use the Performance tab in Vue Devtools to analyze component rendering times. Programmatically measure specific operations:
import { performance } from 'perf_hooks'
function measure(fn) {
const start = performance.now()
const result = fn()
console.log(`Time taken: ${performance.now() - start}ms`)
return result
}
// Usage example
measure(() => {
app.mount('#app')
})
For production environments, use window.performance.mark
to record critical timings:
export function trackTiming(name) {
if (process.env.NODE_ENV === 'production') {
performance.mark(`${name}-start`)
return () => {
performance.mark(`${name}-end`)
performance.measure(name, `${name}-start`, `${name}-end`)
}
}
return () => {}
}
// Usage example
const endTracking = trackTiming('data-loading')
loadData().finally(endTracking)
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:组件渲染优化策略