Skip to content

浏览器渲染优化案例大全

本文档整理了50个常见的浏览器渲染优化案例,涵盖CSS、JavaScript、DOM操作、图片、动画等多个方面。

目录

一、CSS优化(1-8)

二、JavaScript优化(9-16)

三、DOM操作优化(17-24)

四、图片优化(25-32)

五、字体优化(33-36)

六、动画优化(37-42)

七、渲染层优化(43-46)

八、网络与资源优化(47-50)


一、CSS优化

案例1:避免使用@import引入CSS

问题描述: 使用@import引入CSS会导致额外的网络请求延迟,因为浏览器必须先下载主CSS文件,解析后才能发现@import语句,再发起额外请求。

问题代码:

css
/* style.css */
@import url('reset.css');
@import url('header.css');
@import url('footer.css');

优化方案: 使用<link>标签并行加载CSS文件。

html
<link rel="stylesheet" href="reset.css" />
<link rel="stylesheet" href="header.css" />
<link rel="stylesheet" href="footer.css" />

性能收益:

  • 减少网络请求瀑布流
  • CSS文件可并行下载
  • 页面渲染提前约200-500ms

案例2:避免CSS表达式

问题描述: CSS表达式(IE特有)会在页面滚动、重绘等事件中反复执行,造成严重性能问题。

问题代码:

css
.box {
  width: expression(document.body.clientWidth > 800 ? '800px': '100%');
}

优化方案: 使用JavaScript或媒体查询替代。

css
.box {
  width: 100%;
}

@media (min-width: 800px) {
  .box {
    width: 800px;
  }
}

性能收益:

  • 避免频繁的JavaScript执行
  • 减少CPU占用
  • 提升页面流畅度

案例3:使用will-change优化动画性能

问题描述: 动画元素在每一帧都需要重新合成,导致性能下降。

问题代码:

css
.animated-box {
  animation: slide 2s ease-in-out;
}

@keyframes slide {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}

优化方案: 提前告知浏览器元素将要变化,创建独立的合成层。

css
.animated-box {
  will-change: transform;
  animation: slide 2s ease-in-out;
}

@keyframes slide {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}

注意事项:

  • 不要滥用will-change,只在需要时使用
  • 动画结束后移除will-change属性
javascript
element.addEventListener('animationend', () => {
  element.style.willChange = 'auto'
})

性能收益:

  • 动画帧率提升至60fps
  • 减少主线程压力
  • 避免动画卡顿

案例4:避免强制同步布局

问题描述: 在修改DOM后立即读取布局属性,会强制浏览器同步计算布局,造成性能问题。

问题代码:

javascript
const boxes = document.querySelectorAll('.box')
boxes.forEach(box => {
  box.style.width = '100px'
  const height = box.offsetHeight // 强制同步布局
  box.style.height = height + 10 + 'px'
})

优化方案: 先批量读取,再批量写入。

javascript
const boxes = document.querySelectorAll('.box')
const heights = []

// 批量读取
boxes.forEach(box => {
  heights.push(box.offsetHeight)
})

// 批量写入
boxes.forEach((box, index) => {
  box.style.width = '100px'
  box.style.height = heights[index] + 10 + 'px'
})

性能收益:

  • 减少布局重计算次数
  • 避免布局抖动
  • 页面响应速度提升50%以上

案例5:减少重排重绘

问题描述: 频繁修改DOM样式导致多次重排和重绘,影响渲染性能。

问题代码:

javascript
const element = document.getElementById('box')
element.style.width = '100px'
element.style.height = '100px'
element.style.margin = '10px'
element.style.padding = '10px'
element.style.border = '1px solid red'

优化方案: 使用CSS类切换或cssText批量修改。

javascript
// 方案1:使用CSS类
const element = document.getElementById('box');
element.classList.add('styled-box');

// CSS
.styled-box {
  width: 100px;
  height: 100px;
  margin: 10px;
  padding: 10px;
  border: 1px solid red;
}

// 方案2:使用cssText
element.style.cssText = `
  width: 100px;
  height: 100px;
  margin: 10px;
  padding: 10px;
  border: 1px solid red;
`;

性能收益:

  • 减少重排次数
  • 提升渲染效率
  • 降低CPU使用率

案例6:使用CSS Containment

问题描述: 页面中某个区域的样式变化会影响整个页面的布局计算。

问题代码:

html
<div class="widget">
  <!-- 复杂的组件内容 -->
</div>

优化方案: 使用contain属性隔离组件的渲染影响范围。

css
.widget {
  contain: layout style paint;
}

/* 或使用更严格的隔离 */
.isolated-widget {
  contain: strict;
}

contain属性值说明:

  • layout:元素的布局不影响外部
  • paint:元素不会溢出边界
  • style:样式不会影响外部
  • strict:等同于 layout paint style
  • content:等同于 layout paint

性能收益:

  • 减少布局计算范围
  • 提升页面渲染效率
  • 特别适合复杂组件

案例7:优化选择器性能

问题描述: 复杂的CSS选择器会增加样式匹配时间,影响渲染性能。

问题代码:

css
/* 过于复杂的选择器 */
body div.container ul li a span.icon {
  color: red;
}

/* 使用通配符 */
* {
  box-sizing: border-box;
}

/* 属性选择器开头 */
[class^='icon-'] {
  font-size: 16px;
}

优化方案: 使用简洁、具体的类选择器。

css
/* 简洁的类选择器 */
.icon {
  color: red;
}

/* 避免通配符,使用继承 */
html {
  box-sizing: border-box;
}
*,
*::before,
*::after {
  box-sizing: inherit;
}

/* 使用具体的类名 */
.icon-small {
  font-size: 14px;
}
.icon-medium {
  font-size: 16px;
}
.icon-large {
  font-size: 18px;
}

性能收益:

  • 减少样式匹配时间
  • 降低CSS解析开销
  • 提升首次渲染速度

案例8:使用transform替代top/left

问题描述: 使用topleft等属性进行动画会触发重排,性能较差。

问题代码:

css
.moving-box {
  position: absolute;
  top: 0;
  left: 0;
  transition:
    top 0.3s,
    left 0.3s;
}

.moving-box.active {
  top: 100px;
  left: 100px;
}

优化方案: 使用transform属性,只触发合成,不触发重排。

css
.moving-box {
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 0.3s;
}

.moving-box.active {
  transform: translate(100px, 100px);
}

性能收益:

  • 动画流畅度提升
  • 避免重排
  • 可利用GPU加速

二、JavaScript优化

案例9:使用requestAnimationFrame

问题描述: 使用setTimeoutsetInterval执行动画,可能导致帧丢失或卡顿。

问题代码:

javascript
function animate() {
  updatePosition()
  setTimeout(animate, 16) // 约60fps
}
animate()

优化方案: 使用requestAnimationFrame,与浏览器刷新率同步。

javascript
function animate() {
  updatePosition()
  requestAnimationFrame(animate)
}
animate()

// 带时间戳的精确动画
let startTime = null
function animate(timestamp) {
  if (!startTime) startTime = timestamp
  const progress = timestamp - startTime

  updatePosition(progress)

  if (progress < 2000) {
    requestAnimationFrame(animate)
  }
}
requestAnimationFrame(animate)

性能收益:

  • 与显示器刷新率同步
  • 页面不可见时自动暂停
  • 更流畅的动画效果

案例10:防抖与节流

问题描述: 高频事件(如scroll、resize、input)频繁触发回调,导致性能问题。

问题代码:

javascript
window.addEventListener('scroll', () => {
  updateLayout() // 每次滚动都执行
})

input.addEventListener('input', () => {
  search(query) // 每次输入都请求
})

优化方案: 使用防抖或节流控制执行频率。

javascript
// 防抖:延迟执行,重复触发重新计时
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(updateLayout, 100))
input.addEventListener('input', debounce(search, 300))

性能收益:

  • 减少函数执行次数
  • 降低CPU使用率
  • 提升页面响应速度

案例11:避免长任务阻塞主线程

问题描述: 长时间的JavaScript执行会阻塞主线程,导致页面无响应。

问题代码:

javascript
function processLargeArray(array) {
  array.forEach(item => {
    // 复杂处理
    heavyComputation(item)
  })
}

优化方案: 使用requestIdleCallback或分片处理。

javascript
// 方案1:使用requestIdleCallback
function processLargeArray(array) {
  let index = 0

  function processChunk(deadline) {
    while (index < array.length && deadline.timeRemaining() > 0) {
      heavyComputation(array[index])
      index++
    }

    if (index < array.length) {
      requestIdleCallback(processChunk)
    }
  }

  requestIdleCallback(processChunk)
}

// 方案2:使用setTimeout分片
function processLargeArray(array, chunkSize = 100) {
  let index = 0

  function processChunk() {
    const end = Math.min(index + chunkSize, array.length)

    while (index < end) {
      heavyComputation(array[index])
      index++
    }

    if (index < array.length) {
      setTimeout(processChunk, 0)
    }
  }

  processChunk()
}

性能收益:

  • 保持页面响应
  • 避免卡顿
  • 更好的用户体验

案例12:使用Web Worker处理复杂计算

问题描述: 复杂计算占用主线程,导致页面卡顿。

问题代码:

javascript
// 主线程中执行复杂计算
function calculate() {
  const result = heavyCalculation(data)
  updateUI(result)
}

优化方案: 将计算移至Web Worker。

javascript
// main.js
const worker = new Worker('worker.js')

worker.postMessage({ data: largeData })

worker.onmessage = function (e) {
  updateUI(e.data.result)
}

// worker.js
self.onmessage = function (e) {
  const result = heavyCalculation(e.data.data)
  self.postMessage({ result })
}

性能收益:

  • 主线程保持响应
  • 充分利用多核CPU
  • 提升计算效率

案例13:优化事件监听器

问题描述: 大量元素绑定相同的事件处理函数,占用内存且效率低。

问题代码:

javascript
const buttons = document.querySelectorAll('.btn')
buttons.forEach(btn => {
  btn.addEventListener('click', handleClick)
})

优化方案: 使用事件委托,在父元素上统一处理。

javascript
// 事件委托
document.getElementById('button-container').addEventListener('click', e => {
  if (e.target.matches('.btn')) {
    handleClick(e)
  }
})

// 使用passive提升滚动性能
document.addEventListener('scroll', handleScroll, { passive: true })

// 使用once自动移除
element.addEventListener('click', handleClick, { once: true })

性能收益:

  • 减少内存占用
  • 动态元素自动支持
  • 提升事件处理效率

案例14:使用Intersection Observer懒加载

问题描述: 使用scroll事件检测元素可见性,性能差且不精确。

问题代码:

javascript
window.addEventListener('scroll', () => {
  const images = document.querySelectorAll('img[data-src]')
  images.forEach(img => {
    const rect = img.getBoundingClientRect()
    if (rect.top < window.innerHeight) {
      img.src = img.dataset.src
    }
  })
})

优化方案: 使用Intersection Observer API。

javascript
const observer = new IntersectionObserver(
  entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target
        img.src = img.dataset.src
        observer.unobserve(img)
      }
    })
  },
  {
    rootMargin: '50px',
    threshold: 0.1
  }
)

document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img)
})

性能收益:

  • 避免scroll事件开销
  • 精确的可见性检测
  • 更好的性能表现

案例15:避免内存泄漏

问题描述: 未正确清理的引用导致内存无法回收。

问题代码:

javascript
// 闭包导致的内存泄漏
function createHandler() {
  const largeData = new Array(1000000).fill('x')
  return function () {
    console.log(largeData.length)
  }
}

// 未移除的事件监听器
const handler = () => {
  /* ... */
}
element.addEventListener('click', handler)
// 元素移除时未移除监听器

优化方案: 正确管理引用和清理资源。

javascript
// 避免不必要的闭包
function createHandler() {
  const length = 1000000
  return function () {
    console.log(length)
  }
}

// 使用WeakMap避免强引用
const cache = new WeakMap()

// 正确移除事件监听器
const handler = () => {
  /* ... */
}
element.addEventListener('click', handler)

// 清理时
element.removeEventListener('click', handler)
element.remove()

// 使用AbortController批量移除
const controller = new AbortController()
element.addEventListener('click', handler, {
  signal: controller.signal
})

// 批量移除
controller.abort()

性能收益:

  • 避免内存泄漏
  • 保持页面稳定运行
  • 减少内存占用

案例16:使用虚拟列表优化大数据渲染

问题描述: 渲染大量列表项导致DOM节点过多,页面卡顿。

问题代码:

javascript
function renderList(data) {
  const container = document.getElementById('list')
  data.forEach(item => {
    const li = document.createElement('li')
    li.textContent = item.name
    container.appendChild(li)
  })
}

// 渲染10000条数据
renderList(largeData)

优化方案: 使用虚拟列表,只渲染可见区域的元素。

javascript
class VirtualList {
  constructor(options) {
    this.container = options.container
    this.itemHeight = options.itemHeight
    this.data = options.data
    this.visibleCount =
      Math.ceil(this.container.clientHeight / this.itemHeight) + 2
    this.startIndex = 0

    this.init()
  }

  init() {
    this.wrapper = document.createElement('div')
    this.wrapper.style.cssText = `
      position: relative;
      height: ${this.data.length * this.itemHeight}px;
    `
    this.container.appendChild(this.wrapper)

    this.renderItems()
    this.container.addEventListener('scroll', this.handleScroll.bind(this))
  }

  handleScroll() {
    const scrollTop = this.container.scrollTop
    this.startIndex = Math.floor(scrollTop / this.itemHeight)
    this.renderItems()
  }

  renderItems() {
    const endIndex = Math.min(
      this.startIndex + this.visibleCount,
      this.data.length
    )
    this.wrapper.innerHTML = ''

    for (let i = this.startIndex; i < endIndex; i++) {
      const item = document.createElement('div')
      item.style.cssText = `
        position: absolute;
        top: ${i * this.itemHeight}px;
        height: ${this.itemHeight}px;
        width: 100%;
      `
      item.textContent = this.data[i].name
      this.wrapper.appendChild(item)
    }
  }
}

// 使用
new VirtualList({
  container: document.getElementById('list'),
  itemHeight: 50,
  data: largeData
})

性能收益:

  • DOM节点数量恒定
  • 支持百万级数据渲染
  • 滚动流畅不卡顿

三、DOM操作优化

案例17:批量DOM操作

问题描述: 循环中直接操作DOM,导致多次重排重绘。

问题代码:

javascript
const list = document.getElementById('list')
for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li')
  item.textContent = `Item ${i}`
  list.appendChild(item)
}

优化方案: 使用DocumentFragment批量添加。

javascript
const list = document.getElementById('list')
const fragment = document.createDocumentFragment()

for (let i = 0; i < 1000; i++) {
  const item = document.createElement('li')
  item.textContent = `Item ${i}`
  fragment.appendChild(item)
}

list.appendChild(fragment)

性能收益:

  • 减少重排次数
  • 提升渲染效率
  • 执行时间减少90%以上

案例18:使用DocumentFragment

问题描述: 频繁向DOM添加节点,每次都触发重排。

问题代码:

javascript
const container = document.getElementById('container')
;['header', 'content', 'footer'].forEach(name => {
  const section = document.createElement('section')
  section.className = name
  container.appendChild(section)
})

优化方案: 使用DocumentFragment作为临时容器。

javascript
const container = document.getElementById('container')
const fragment = document.createDocumentFragment()

;['header', 'content', 'footer'].forEach(name => {
  const section = document.createElement('section')
  section.className = name
  fragment.appendChild(section)
})

container.appendChild(fragment)

性能收益:

  • 只触发一次重排
  • 减少页面抖动
  • 提升用户体验

案例19:离线DOM操作

问题描述: 在可见的DOM树上直接操作,导致频繁重排。

问题代码:

javascript
const list = document.getElementById('list')
for (let i = 0; i < 100; i++) {
  const item = document.createElement('li')
  item.textContent = `Item ${i}`
  list.appendChild(item)
}

优化方案: 将元素脱离文档流后操作。

javascript
const list = document.getElementById('list')

// 方案1:隐藏后操作
list.style.display = 'none'
for (let i = 0; i < 100; i++) {
  const item = document.createElement('li')
  item.textContent = `Item ${i}`
  list.appendChild(item)
}
list.style.display = ''

// 方案2:使用cloneNode
const clone = list.cloneNode(true)
for (let i = 0; i < 100; i++) {
  const item = document.createElement('li')
  item.textContent = `Item ${i}`
  clone.appendChild(item)
}
list.parentNode.replaceChild(clone, list)

性能收益:

  • 避免多次重排
  • 操作更高效
  • 页面更稳定

案例20:避免频繁读取布局属性

问题描述: 在循环中交替读取和修改布局属性,导致布局抖动。

问题代码:

javascript
const elements = document.querySelectorAll('.box')
elements.forEach(el => {
  const width = el.offsetWidth
  el.style.width = width + 10 + 'px'
})

优化方案: 批量读取,批量写入。

javascript
const elements = document.querySelectorAll('.box')

// 批量读取
const widths = Array.from(elements).map(el => el.offsetWidth)

// 批量写入
elements.forEach((el, i) => {
  el.style.width = widths[i] + 10 + 'px'
})

性能收益:

  • 避免布局抖动
  • 减少重排次数
  • 执行效率提升数倍

案例21:使用cloneNode批量克隆

问题描述: 重复创建相同结构的DOM元素,效率低下。

问题代码:

javascript
function createItem(text) {
  const item = document.createElement('div')
  item.className = 'item'

  const icon = document.createElement('span')
  icon.className = 'icon'
  item.appendChild(icon)

  const content = document.createElement('span')
  content.className = 'content'
  content.textContent = text
  item.appendChild(content)

  return item
}

for (let i = 0; i < 100; i++) {
  container.appendChild(createItem(`Item ${i}`))
}

优化方案: 使用模板和cloneNode。

javascript
const template = document.createElement('div')
template.className = 'item'
template.innerHTML = `
  <span class="icon"></span>
  <span class="content"></span>
`

for (let i = 0; i < 100; i++) {
  const item = template.cloneNode(true)
  item.querySelector('.content').textContent = `Item ${i}`
  container.appendChild(item)
}

性能收益:

  • 减少DOM创建开销
  • 代码更简洁
  • 执行效率提升

案例22:优化innerHTML操作

问题描述: 使用innerHTML拼接字符串,存在XSS风险且效率不高。

问题代码:

javascript
const list = document.getElementById('list')
let html = ''
for (let i = 0; i < 100; i++) {
  html += `<li>${data[i].name}</li>`
}
list.innerHTML = html

优化方案: 使用模板字符串和DOMPurify防XSS,或使用DocumentFragment。

javascript
// 方案1:使用模板字符串和DOMPurify
const list = document.getElementById('list')
const html = data
  .map(item => `<li>${DOMPurify.sanitize(item.name)}</li>`)
  .join('')
list.innerHTML = html

// 方案2:使用insertAdjacentHTML
list.insertAdjacentHTML('beforeend', html)

// 方案3:使用DocumentFragment(更安全)
const fragment = document.createDocumentFragment()
data.forEach(item => {
  const li = document.createElement('li')
  li.textContent = item.name
  fragment.appendChild(li)
})
list.appendChild(fragment)

性能收益:

  • 避免XSS攻击
  • 提升安全性
  • 操作更高效

案例23:使用事件委托

问题描述: 为每个子元素单独绑定事件,内存占用大且不支持动态元素。

问题代码:

javascript
const items = document.querySelectorAll('.list-item')
items.forEach(item => {
  item.addEventListener('click', e => {
    handleClick(e.target.dataset.id)
  })
})

优化方案: 在父元素上使用事件委托。

javascript
const list = document.getElementById('list')
list.addEventListener('click', e => {
  const item = e.target.closest('.list-item')
  if (item) {
    handleClick(item.dataset.id)
  }
})

性能收益:

  • 减少事件监听器数量
  • 支持动态添加的元素
  • 内存占用大幅降低

案例24:最小化DOM访问

问题描述: 频繁访问DOM属性,每次访问都有性能开销。

问题代码:

javascript
const element = document.getElementById('box')
element.style.width = element.offsetWidth + 10 + 'px'
element.style.height = element.offsetHeight + 10 + 'px'
element.style.left = element.offsetLeft + 10 + 'px'
element.style.top = element.offsetTop + 10 + 'px'

优化方案: 缓存DOM引用和计算结果。

javascript
const element = document.getElementById('box')

// 缓存布局信息
const rect = element.getBoundingClientRect()

// 批量设置样式
element.style.cssText = `
  width: ${rect.width + 10}px;
  height: ${rect.height + 10}px;
  left: ${rect.left + 10}px;
  top: ${rect.top + 10}px;
`

性能收益:

  • 减少DOM访问次数
  • 降低布局计算开销
  • 提升执行效率

四、图片优化

案例25:使用懒加载图片

问题描述: 一次性加载所有图片,导致页面加载缓慢。

问题代码:

html
<img src="image1.jpg" alt="" />
<img src="image2.jpg" alt="" />
<img src="image3.jpg" alt="" />
<!-- 更多图片 -->

优化方案: 使用原生懒加载或Intersection Observer。

html
<!-- 方案1:原生懒加载 -->
<img src="image1.jpg" loading="lazy" alt="" />
<img src="image2.jpg" loading="lazy" alt="" />

<!-- 方案2:使用data-src -->
<img data-src="image1.jpg" src="placeholder.jpg" alt="" />
<img data-src="image2.jpg" src="placeholder.jpg" alt="" />
javascript
// Intersection Observer实现
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' }
)

document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img)
})

性能收益:

  • 减少初始加载时间
  • 节省带宽
  • 提升页面加载速度

案例26:使用响应式图片

问题描述: 同一张图片在不同设备上显示,浪费带宽或显示模糊。

问题代码:

html
<img src="hero-image.jpg" alt="Hero" />

优化方案: 使用srcset和sizes属性。

html
<!-- 使用srcset -->
<img
  src="hero-800.jpg"
  srcset="
    hero-400.jpg   400w,
    hero-800.jpg   800w,
    hero-1200.jpg 1200w,
    hero-1600.jpg 1600w
  "
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 50vw,
         800px"
  alt="Hero"
/>

<!-- 使用picture元素 -->
<picture>
  <source media="(max-width: 600px)" srcset="hero-mobile.jpg" />
  <source media="(max-width: 1200px)" srcset="hero-tablet.jpg" />
  <img src="hero-desktop.jpg" alt="Hero" />
</picture>

性能收益:

  • 根据设备加载合适尺寸
  • 节省移动端带宽
  • 提升用户体验

案例27:使用WebP格式

问题描述: 传统JPEG/PNG格式图片体积较大。

问题代码:

html
<img src="image.jpg" alt="" />

优化方案: 使用WebP格式并提供fallback。

html
<picture>
  <source srcset="image.avif" type="image/avif" />
  <source srcset="image.webp" type="image/webp" />
  <img src="image.jpg" alt="" />
</picture>

性能收益:

  • 图片体积减少25-35%
  • 加载速度提升
  • 视觉质量保持

案例28:图片预加载

问题描述: 关键图片在需要时才加载,导致延迟。

问题代码:

html
<!-- 用户hover时才加载hover图片 -->
<style>
  .button:hover {
    background-image: url('hover-bg.jpg');
  }
</style>

优化方案: 预加载关键图片。

html
<!-- 使用link预加载 -->
<link rel="preload" href="hero-image.jpg" as="image" />

<!-- 使用JS预加载 -->
<script>
  function preloadImages(urls) {
    urls.forEach(url => {
      const img = new Image()
      img.src = url
    })
  }

  preloadImages(['hover-bg.jpg', 'active-bg.jpg'])
</script>

<!-- 使用CSS预加载 -->
<style>
  .preload {
    background: url('hover-bg.jpg') no-repeat -9999px -9999px;
  }
</style>

性能收益:

  • 关键图片即时显示
  • 提升交互体验
  • 减少等待时间

案例29:使用CSS Sprites

问题描述: 多个小图标分别请求,增加HTTP请求数。

问题代码:

css
.icon-home {
  background-image: url('icon-home.png');
}
.icon-user {
  background-image: url('icon-user.png');
}
.icon-cart {
  background-image: url('icon-cart.png');
}

优化方案: 合并为雪碧图。

css
.icon {
  background-image: url('sprites.png');
  background-repeat: no-repeat;
}

.icon-home {
  background-position: 0 0;
}
.icon-user {
  background-position: -32px 0;
}
.icon-cart {
  background-position: -64px 0;
}

性能收益:

  • 减少HTTP请求
  • 减少图片总大小
  • 提升加载速度

案例30:使用图片占位符

问题描述: 图片加载过程中页面布局抖动。

问题代码:

html
<div class="container">
  <img src="large-image.jpg" alt="" />
</div>

优化方案: 使用占位符或宽高比容器。

html
<!-- 方案1:设置宽高 -->
<img src="large-image.jpg" width="400" height="300" alt="" />

<!-- 方案2:使用aspect-ratio -->
<style>
  .image-container {
    aspect-ratio: 4/3;
    background: #f0f0f0;
  }

  .image-container img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
</style>

<!-- 方案3:使用LQIP(低质量图片占位符)-->
<style>
  .lqip {
    background-size: cover;
    background-position: center;
  }
</style>
<div class="lqip" style="background-image: url('tiny-blur.jpg')">
  <img src="full-image.jpg" loading="lazy" alt="" />
</div>

性能收益:

  • 避免布局抖动
  • 提升用户体验
  • CLS指标优化

案例31:避免图片布局抖动

问题描述: 图片加载前后尺寸变化导致布局抖动。

问题代码:

html
<div class="content">
  <img src="image.jpg" alt="" />
  <p>文字内容</p>
</div>

优化方案: 预留图片空间。

html
<!-- 方案1:使用CSS aspect-ratio -->
<style>
  .content img {
    width: 100%;
    aspect-ratio: 16/9;
    object-fit: cover;
  }
</style>

<!-- 方案2:使用padding-bottom技巧 -->
<style>
  .image-wrapper {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 */
    height: 0;
    overflow: hidden;
  }

  .image-wrapper img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
</style>

<div class="image-wrapper">
  <img src="image.jpg" alt="" />
</div>

性能收益:

  • 避免CLS问题
  • 提升Core Web Vitals
  • 更好的用户体验

案例32:使用AVIF格式

问题描述: 需要更高压缩率的图片格式。

问题代码:

html
<img src="image.jpg" alt="" />

优化方案: 使用AVIF格式并提供fallback。

html
<picture>
  <source srcset="image.avif" type="image/avif" />
  <source srcset="image.webp" type="image/webp" />
  <img src="image.jpg" alt="" />
</picture>

性能收益:

  • 比WebP再小20%
  • 比JPEG小50%
  • 支持HDR和透明度

五、字体优化

案例33:字体预加载

问题描述: 字体加载延迟导致文字显示延迟。

问题代码:

html
<style>
  @font-face {
    font-family: 'CustomFont';
    src: url('custom-font.woff2') format('woff2');
  }
</style>

优化方案: 预加载关键字体。

html
<head>
  <link
    rel="preload"
    href="custom-font.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  />

  <style>
    @font-face {
      font-family: 'CustomFont';
      src: url('custom-font.woff2') format('woff2');
      font-display: swap;
    }
  </style>
</head>

性能收益:

  • 字体更早开始加载
  • 减少文字闪烁
  • 提升首屏渲染

案例34:使用font-display属性

问题描述: 字体加载期间文字不可见或闪烁。

问题代码:

css
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
}

优化方案: 使用font-display控制字体加载行为。

css
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap; /* 立即显示后备字体 */
}

/* font-display选项说明:
 * swap: 立即显示后备字体,字体加载完成后替换
 * block: 等待字体加载(最多3秒),然后使用后备字体
 * fallback: 100ms内等待字体,之后使用后备字体
 * optional: 根据网络状况决定是否使用自定义字体
 */

性能收益:

  • 文字立即可见
  • 减少布局偏移
  • 提升用户体验

案例35:字体子集化

问题描述: 字体文件包含所有字符,体积过大。

问题代码:

css
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font-full.woff2') format('woff2'); /* 包含所有字符 */
}

优化方案: 只包含需要的字符。

css
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font-subset.woff2') format('woff2'); /* 只包含需要的字符 */
  unicode-range: U+4E00-9FFF; /* 中文字符范围 */
}
bash
# 使用工具生成子集
# pyftsubset font.ttf --output-file=font-subset.woff2 \
#   --flavor=woff2 \
#   --layout-features='*' \
#   --text="需要包含的文字"

性能收益:

  • 字体文件体积减少80%+
  • 加载速度大幅提升
  • 节省带宽

案例36:避免字体闪烁

问题描述: 字体加载完成后文字尺寸变化导致布局偏移。

问题代码:

css
body {
  font-family: 'CustomFont', sans-serif;
}

优化方案: 使用size-adjust和ascent-override匹配后备字体。

css
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap;
  size-adjust: 100%;
  ascent-override: 90%;
  descent-override: 10%;
  line-gap-override: 0%;
}

/* 或使用CSS @font-face描述符匹配后备字体 */
@font-face {
  font-family: 'CustomFont-fallback';
  src: local('Arial');
  size-adjust: 98%;
  ascent-override: 92%;
}

body {
  font-family: 'CustomFont', 'CustomFont-fallback', sans-serif;
}

性能收益:

  • 减少布局偏移
  • 提升CLS指标
  • 更好的视觉稳定性

六、动画优化

案例37:使用CSS动画替代JS动画

问题描述: 使用JavaScript实现动画,占用主线程资源。

问题代码:

javascript
function animate(element, start, end, duration) {
  const startTime = performance.now()

  function update(currentTime) {
    const elapsed = currentTime - startTime
    const progress = Math.min(elapsed / duration, 1)

    element.style.left = start + (end - start) * progress + 'px'

    if (progress < 1) {
      requestAnimationFrame(update)
    }
  }

  requestAnimationFrame(update)
}

优化方案: 使用CSS动画或过渡。

css
.box {
  position: absolute;
  left: 0;
  transition: left 0.3s ease-out;
}

.box.moved {
  left: 100px;
}

/* 或使用CSS动画 */
@keyframes slide {
  from {
    left: 0;
  }
  to {
    left: 100px;
  }
}

.box.animated {
  animation: slide 0.3s ease-out forwards;
}

性能收益:

  • 动画在合成线程执行
  • 不阻塞主线程
  • 更流畅的动画效果

案例38:使用GPU加速

问题描述: 动画在CPU上执行,性能较差。

问题代码:

css
.box {
  animation: move 2s ease-in-out;
}

@keyframes move {
  from {
    left: 0;
  }
  to {
    left: 100px;
  }
}

优化方案: 使用transform和opacity触发GPU加速。

css
.box {
  will-change: transform;
  animation: move 2s ease-in-out;
}

@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}

/* 或使用translate3d强制GPU加速 */
.box {
  transform: translate3d(0, 0, 0);
}

性能收益:

  • 动画在GPU上执行
  • 主线程保持响应
  • 60fps流畅动画

案例39:避免动画布局抖动

问题描述: 动画属性导致频繁重排。

问题代码:

css
@keyframes expand {
  from {
    width: 100px;
    height: 100px;
  }
  to {
    width: 200px;
    height: 200px;
  }
}

优化方案: 使用transform替代尺寸属性。

css
@keyframes expand {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(2);
  }
}

/* 或使用transform-origin控制缩放中心 */
.box {
  transform-origin: top left;
  animation: expand 0.3s ease-out;
}

性能收益:

  • 避免重排
  • 动画更流畅
  • 性能提升明显

案例40:使用CSS变量优化动画

问题描述: 复杂动画需要大量重复代码。

问题代码:

css
.box1 {
  animation: slide 1s ease-out;
}
.box2 {
  animation: slide 1.5s ease-out;
}
.box3 {
  animation: slide 2s ease-out;
}

优化方案: 使用CSS变量动态控制动画。

css
.box {
  --duration: 1s;
  --delay: 0s;
  animation: slide var(--duration) ease-out var(--delay);
}

.box1 { --duration: 1s; }
.box2 { --duration: 1.5s; --delay: 0.1s; }
.box3 { --duration: 2s; --delay: 0.2s; }

/* JavaScript动态控制 */
element.style.setProperty('--duration', '1.5s');

性能收益:

  • 代码更简洁
  • 动态控制更方便
  • 维护成本降低

案例41:优化滚动动画

问题描述: 滚动事件中执行复杂计算导致卡顿。

问题代码:

javascript
window.addEventListener('scroll', () => {
  const scrollTop = window.scrollY
  document.querySelectorAll('.animate-on-scroll').forEach(el => {
    const rect = el.getBoundingClientRect()
    if (rect.top < window.innerHeight) {
      el.classList.add('visible')
    }
  })
})

优化方案: 使用Intersection Observer和CSS动画。

javascript
const observer = new IntersectionObserver(
  entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('visible')
      }
    })
  },
  { threshold: 0.1 }
)

document.querySelectorAll('.animate-on-scroll').forEach(el => {
  observer.observe(el)
})
css
.animate-on-scroll {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity 0.3s,
    transform 0.3s;
}

.animate-on-scroll.visible {
  opacity: 1;
  transform: translateY(0);
}

性能收益:

  • 避免scroll事件开销
  • 更精确的触发时机
  • 流畅的滚动体验

案例42:使用Web Animations API

问题描述: 需要JavaScript控制复杂动画序列。

问题代码:

javascript
element.style.transition = 'all 0.3s'
element.style.opacity = '0'

setTimeout(() => {
  element.style.display = 'none'
}, 300)

优化方案: 使用Web Animations API。

javascript
const animation = element.animate(
  [
    { opacity: 1, transform: 'scale(1)' },
    { opacity: 0, transform: 'scale(0.9)' }
  ],
  {
    duration: 300,
    easing: 'ease-out',
    fill: 'forwards'
  }
)

animation.onfinish = () => {
  element.style.display = 'none'
}

// 支持Promise
animation.finished.then(() => {
  console.log('Animation completed')
})

性能收益:

  • 更强大的动画控制
  • 支持Promise
  • 浏览器优化的动画引擎

七、渲染层优化

案例43:创建渲染层

问题描述: 复杂元素与其他元素共享渲染层,导致不必要的重绘。

问题代码:

css
.complex-element {
  /* 复杂的样式 */
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  filter: blur(5px);
}

优化方案: 创建独立的渲染层。

css
.complex-element {
  /* 触发层创建的属性 */
  transform: translateZ(0);
  /* 或 */
  will-change: transform;
  /* 或 */
  backface-visibility: hidden;

  box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  filter: blur(5px);
}

性能收益:

  • 减少重绘范围
  • 提升渲染效率
  • 动画更流畅

案例44:避免层爆炸

问题描述: 过多元素创建独立渲染层,占用大量内存。

问题代码:

css
* {
  transform: translateZ(0);
}

优化方案: 只为需要的元素创建渲染层。

css
/* 只为动画元素创建层 */
.animated-element {
  will-change: transform;
  animation: slide 1s ease-out;
}

/* 动画结束后移除will-change */
.animated-element.animation-ended {
  will-change: auto;
}

性能收益:

  • 减少内存占用
  • 避免合成开销
  • 整体性能提升

案例45:使用opacity优化层合成

问题描述: 使用visibility隐藏元素仍占用渲染资源。

问题代码:

css
.hidden {
  visibility: hidden;
}

优化方案: 使用opacity配合pointer-events。

css
.hidden {
  opacity: 0;
  pointer-events: none;
}

/* 显示时 */
.visible {
  opacity: 1;
  pointer-events: auto;
  transition: opacity 0.3s;
}

性能收益:

  • opacity不触发重绘
  • 动画更流畅
  • 过渡效果更好

案例46:使用Fixed定位创建层

问题描述: 固定定位元素随页面滚动重绘。

问题代码:

css
.header {
  position: fixed;
  top: 0;
  width: 100%;
}

优化方案: 确保固定元素创建独立渲染层。

css
.header {
  position: fixed;
  top: 0;
  width: 100%;
  will-change: transform;
  /* 或 */
  transform: translateZ(0);
  /* 确保有明确的z-index */
  z-index: 100;
}

性能收益:

  • 避免滚动时重绘
  • 提升滚动性能
  • 减少CPU使用

八、网络与资源优化

案例47:使用预加载preload

问题描述: 关键资源发现太晚,延迟加载。

问题代码:

html
<head>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <!-- 字体在CSS中发现,加载延迟 -->
  <script src="app.js"></script>
</body>

优化方案: 预加载关键资源。

html
<head>
  <!-- 预加载关键CSS -->
  <link rel="preload" href="style.css" as="style" />

  <!-- 预加载关键字体 -->
  <link
    rel="preload"
    href="font.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  />

  <!-- 预加载关键脚本 -->
  <link rel="preload" href="app.js" as="script" />

  <!-- 预加载关键图片 -->
  <link rel="preload" href="hero.jpg" as="image" />

  <link rel="stylesheet" href="style.css" />
</head>

性能收益:

  • 资源更早开始加载
  • 减少加载延迟
  • 提升首屏渲染速度

案例48:使用预连接preconnect

问题描述: 连接第三方域名需要DNS查询、TCP握手、TLS协商,耗时较长。

问题代码:

html
<script src="https://cdn.example.com/library.js"></script>

优化方案: 提前建立连接。

html
<head>
  <!-- 预连接CDN -->
  <link rel="preconnect" href="https://cdn.example.com" />

  <!-- 预连接API服务器 -->
  <link rel="preconnect" href="https://api.example.com" />

  <!-- DNS预解析(更轻量) -->
  <link rel="dns-prefetch" href="https://analytics.example.com" />
</head>

性能收益:

  • 节省连接建立时间
  • 减少资源加载延迟
  • 提升页面加载速度

案例49:使用预获取prefetch

问题描述: 下一页资源在需要时才加载。

问题代码:

html
<a href="/next-page">下一页</a>

优化方案: 预获取可能需要的资源。

html
<head>
  <!-- 预获取下一页 -->
  <link rel="prefetch" href="/next-page" as="document" />

  <!-- 预获取下一页需要的资源 -->
  <link rel="prefetch" href="next-page.js" as="script" />
  <link rel="prefetch" href="next-page.css" as="style" />
</head>

<!-- 或使用JavaScript动态预获取 -->
<script>
  const link = document.createElement('link')
  link.rel = 'prefetch'
  link.href = '/next-page'
  document.head.appendChild(link)
</script>

性能收益:

  • 提前加载资源
  • 页面切换更流畅
  • 提升用户体验

案例50:关键CSS内联

问题描述: 外部CSS阻塞渲染,延迟首屏显示。

问题代码:

html
<head>
  <link rel="stylesheet" href="style.css" />
  <!-- 阻塞渲染 -->
</head>

优化方案: 内联关键CSS,异步加载非关键CSS。

html
<head>
  <style>
    /* 内联首屏关键CSS */
    body {
      margin: 0;
      font-family: sans-serif;
    }
    .header {
      height: 60px;
      background: #333;
    }
    .hero {
      height: 400px;
      background: #f0f0f0;
    }
  </style>

  <!-- 异步加载非关键CSS -->
  <link
    rel="preload"
    href="style.css"
    as="style"
    onload="this.rel='stylesheet'"
  />
  <noscript><link rel="stylesheet" href="style.css" /></noscript>
</head>

性能收益:

  • 首屏立即渲染
  • 减少渲染阻塞
  • 提升FCP/LCP指标

总结

以上50个浏览器渲染优化案例涵盖了前端开发的各个方面。在实际项目中,建议:

  1. 性能监控:使用Lighthouse、Chrome DevTools等工具持续监控性能
  2. 按需优化:根据性能瓶颈选择合适的优化方案
  3. 权衡取舍:考虑优化成本和收益,避免过度优化
  4. 持续迭代:性能优化是一个持续的过程,需要不断改进

通过合理应用这些优化技巧,可以显著提升页面的渲染性能和用户体验。

基于 VitePress 的本地知识库