性能优化方向指南
前端性能优化的主要方向和实践方案
问题
前端性能优化有哪些方向?如何系统地进行性能优化?
解答
1. 加载性能优化
资源压缩与合并
// webpack 配置示例
module.exports = {
optimization: {
// 代码分割
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name: 'vendors'
}
}
},
// 压缩
minimize: true
}
}
图片懒加载
// 使用 Intersection Observer 实现懒加载
function lazyLoadImages() {
const images = document.querySelectorAll('img[data-src]')
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
img.removeAttribute('data-src')
observer.unobserve(img)
}
})
}, {
rootMargin: '100px' // 提前 100px 加载
})
images.forEach(img => observer.observe(img))
}
资源预加载
<!-- 预加载关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="main.js" as="script">
<!-- 预连接第三方域名 -->
<link rel="preconnect" href="https://cdn.example.com">
<!-- 预获取下一页资源 -->
<link rel="prefetch" href="next-page.js">
2. 渲染性能优化
防抖与节流
// 防抖:延迟执行,重复触发会重置计时
function debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
// 节流:固定间隔执行一次
function throttle(fn, interval) {
let lastTime = 0
return function(...args) {
const now = Date.now()
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// 使用示例
window.addEventListener('scroll', throttle(handleScroll, 100))
input.addEventListener('input', debounce(search, 300))
减少重排重绘
// 批量修改 DOM
function batchUpdate(items) {
// 使用 DocumentFragment
const fragment = document.createDocumentFragment()
items.forEach(item => {
const li = document.createElement('li')
li.textContent = item
fragment.appendChild(li)
})
list.appendChild(fragment)
}
// 批量修改样式
function updateStyles(el) {
// 不好:多次触发重排
// el.style.width = '100px'
// el.style.height = '100px'
// el.style.margin = '10px'
// 好:一次性修改
el.style.cssText = 'width: 100px; height: 100px; margin: 10px;'
// 或使用 class
el.className = 'updated-style'
}
虚拟列表
// 简易虚拟列表实现
class VirtualList {
constructor(container, itemHeight, totalCount, renderItem) {
this.container = container
this.itemHeight = itemHeight
this.totalCount = totalCount
this.renderItem = renderItem
this.visibleCount = Math.ceil(container.clientHeight / itemHeight)
this.startIndex = 0
this.init()
}
init() {
// 创建占位元素
this.placeholder = document.createElement('div')
this.placeholder.style.height = `${this.totalCount * this.itemHeight}px`
// 创建内容容器
this.content = document.createElement('div')
this.content.style.position = 'relative'
this.container.appendChild(this.placeholder)
this.placeholder.appendChild(this.content)
this.container.addEventListener('scroll', () => this.onScroll())
this.render()
}
onScroll() {
const scrollTop = this.container.scrollTop
const newStartIndex = Math.floor(scrollTop / this.itemHeight)
if (newStartIndex !== this.startIndex) {
this.startIndex = newStartIndex
this.render()
}
}
render() {
// 只渲染可见区域 + 缓冲区
const buffer = 5
const start = Math.max(0, this.startIndex - buffer)
const end = Math.min(this.totalCount, this.startIndex + this.visibleCount + buffer)
this.content.innerHTML = ''
this.content.style.transform = `translateY(${start * this.itemHeight}px)`
for (let i = start; i < end; i++) {
const item = this.renderItem(i)
item.style.height = `${this.itemHeight}px`
this.content.appendChild(item)
}
}
}
3. 网络性能优化
请求优化
// 请求合并
async function batchRequest(ids) {
// 不好:多个请求
// const results = await Promise.all(ids.map(id => fetch(`/api/item/${id}`)))
// 好:合并为一个请求
const result = await fetch('/api/items', {
method: 'POST',
body: JSON.stringify({ ids })
})
return result.json()
}
// 请求缓存
const cache = new Map()
async function fetchWithCache(url, ttl = 60000) {
const cached = cache.get(url)
if (cached && Date.now() - cached.time < ttl) {
return cached.data
}
const data = await fetch(url).then(r => r.json())
cache.set(url, { data, time: Date.now() })
return data
}
4. 代码层面优化
// 避免内存泄漏
class Component {
constructor() {
this.handler = this.handleClick.bind(this)
document.addEventListener('click', this.handler)
}
// 组件销毁时移除监听
destroy() {
document.removeEventListener('click', this.handler)
}
handleClick() {}
}
// 使用 Web Worker 处理耗时任务
// worker.js
self.onmessage = function(e) {
const result = heavyComputation(e.data)
self.postMessage(result)
}
// main.js
const worker = new Worker('worker.js')
worker.postMessage(data)
worker.onmessage = (e) => {
console.log('计算结果:', e.data)
}
5. 构建优化
// vite.config.js
export default {
build: {
// 代码分割
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'vue-router'],
utils: ['lodash-es', 'dayjs']
}
}
},
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true // 移除 console
}
}
}
}
关键点
- 加载优化:压缩资源、懒加载、预加载、使用 CDN、合理缓存
- 渲染优化:减少重排重绘、使用虚拟列表、防抖节流
- 网络优化:减少请求数、使用 HTTP/2、开启 Gzip
- 代码优化:避免内存泄漏、耗时任务用 Web Worker
- 构建优化:Tree Shaking、代码分割、按需加载
目录