Appearance
浏览器渲染优化案例大全
本文档整理了50个常见的浏览器渲染优化案例,涵盖CSS、JavaScript、DOM操作、图片、动画等多个方面。
目录
一、CSS优化(1-8)
- 案例1:避免使用@import引入CSS
- 案例2:避免CSS表达式
- 案例3:使用will-change优化动画性能
- 案例4:避免强制同步布局
- 案例5:减少重排重绘
- 案例6:使用CSS Containment
- 案例7:优化选择器性能
- 案例8:使用transform替代top/left
二、JavaScript优化(9-16)
- 案例9:使用requestAnimationFrame
- 案例10:防抖与节流
- 案例11:避免长任务阻塞主线程
- 案例12:使用Web Worker处理复杂计算
- 案例13:优化事件监听器
- 案例14:使用Intersection Observer懒加载
- 案例15:避免内存泄漏
- 案例16:使用虚拟列表优化大数据渲染
三、DOM操作优化(17-24)
- 案例17:批量DOM操作
- 案例18:使用DocumentFragment
- 案例19:离线DOM操作
- 案例20:避免频繁读取布局属性
- 案例21:使用cloneNode批量克隆
- 案例22:优化innerHTML操作
- 案例23:使用事件委托
- 案例24:最小化DOM访问
四、图片优化(25-32)
- 案例25:使用懒加载图片
- 案例26:使用响应式图片
- 案例27:使用WebP格式
- 案例28:图片预加载
- 案例29:使用CSS Sprites
- 案例30:使用图片占位符
- 案例31:避免图片布局抖动
- 案例32:使用AVIF格式
五、字体优化(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 stylecontent:等同于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
问题描述: 使用top、left等属性进行动画会触发重排,性能较差。
问题代码:
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
问题描述: 使用setTimeout或setInterval执行动画,可能导致帧丢失或卡顿。
问题代码:
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个浏览器渲染优化案例涵盖了前端开发的各个方面。在实际项目中,建议:
- 性能监控:使用Lighthouse、Chrome DevTools等工具持续监控性能
- 按需优化:根据性能瓶颈选择合适的优化方案
- 权衡取舍:考虑优化成本和收益,避免过度优化
- 持续迭代:性能优化是一个持续的过程,需要不断改进
通过合理应用这些优化技巧,可以显著提升页面的渲染性能和用户体验。