渲染和运行时✅
如何优化 DOM 来提升性能?
答案
DOM 对性能的影响主要体现在
- 大量的 DOM 节点,导致首帧渲染耗时变长,因为需要更多的时间来进行布局和绘制。
- DOM 树的大批量修改,导致的重排(Reflow)和重绘(Repaint),会影响页面的渲染性能。
大量 DOM 节点的问题典型思路如下
- 减少 DOM 节点数量:通过异步加载组件、懒加载等方式,减少初始渲染时的 DOM 节点数量。
- 使用虚拟 DOM:如 React、Vue 等框架,通过虚拟 DOM 技术,减少实际 DOM 操作的次数,提高渲染性能。
- content-visibility 属性,可以让浏览器在元素不可见时跳过其渲染,提高性能。
对于 DOM 树的批量修改,典型思路如下
- 批量更新:将多次 DOM 操作合并为一次操作,减少重排和重绘的次数。比如 react batch 机制等
- 使用 DocumentFragment:在内存中构建 DOM 结构,最后一次性插入到文档中,减少对实际 DOM 的操作次数。
- 使用 CSS 动画:通过 CSS 动画代替 JavaScript 动画,减少 JavaScript 线程的压力,提高性能。
- 使用 requestAnimationFrame 通过闲时操作避免阻塞渲染,减少对主线程的影响。
延伸阅读
- DOM 大小对互动的影响以及应对措施 chrome 针对 DOM 的优化策略
DOM 和 GPU 加速的关系?
答案
DOM 渲染可以使用 GPU 加速,但仅限于特定条件下。浏览器通过分层渲染机制,将符合条件的 DOM 元素提升为独立的合成层(Composite Layer),交由 GPU 处理。
触发 GPU 加速的条件
触发条件 | CSS 属性示例 | 加速类型 |
---|---|---|
3D 变换 | transform: translateZ(0) 、transform3d() | 硬件合成 |
CSS 滤镜 | filter: blur(5px) 、filter: drop-shadow() | GPU 滤镜 |
透明度变化 | opacity 动画 | 合成层优化 |
显式声明 | will-change: transform | 预期变化优化 |
特殊元素 | <video> 、<canvas> 、WebGL | 原生硬件加速 |
代码示例
/* 1. 强制创建合成层 */
.gpu-layer {
transform: translateZ(0); /* 或 translate3d(0,0,0) */
/* 明确告知浏览器该元素会发生变化 */
will-change: transform;
}
/* 2. 动画优化 */
.smooth-animation {
/* ✅ GPU 加速的属性 */
transition: transform 0.3s ease, opacity 0.3s ease;
}
.smooth-animation:hover {
transform: scale(1.1);
opacity: 0.8;
}
/* ❌ 避免这些会触发重排的属性动画 */
.bad-animation {
transition: width 0.3s ease; /* 触发重排 */
}
JavaScript 控制 GPU 加速
// 检测 GPU 加速支持
function checkGPUSupport () {
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
return !!gl
}
// 动态启用 GPU 加速
function enableGPUAcceleration (element) {
// 方法1: 使用 will-change
element.style.willChange = 'transform'
// 方法2: 强制创建合成层
element.style.transform = 'translateZ(0)'
// 动画完成后清理
element.addEventListener('transitionend', () => {
element.style.willChange = 'auto'
})
}
// Canvas 硬件加速
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d', {
alpha: false, // 禁用透明度提升性能
desynchronized: true // 启用低延迟渲染
})
性能监控与调试
// 检测合成层创建
function detectCompositeLayers () {
const layers = document.querySelectorAll('[style*="transform"]')
layers.forEach(layer => {
console.log('潜在合成层:', layer)
console.log('计算样式:', getComputedStyle(layer).transform)
})
}
// 性能测量
function measureRenderPerformance () {
performance.mark('render-start')
requestAnimationFrame(() => {
performance.mark('render-end')
performance.measure('render-time', 'render-start', 'render-end')
const measure = performance.getEntriesByName('render-time')[0]
console.log('渲染耗时:', measure.duration)
})
}
优化策略
- 合理使用 避免过度创建合成层,每个层都消耗显存
- 及时清理 动画结束后移除
will-change
属性 - 层级管理 控制
z-index
避免不必要的层级重排 - 内存监控 使用 DevTools 查看 GPU 内存使用情况
/* 最佳实践示例 */
.optimized-element {
/* 只在需要时启用 */
will-change: auto;
}
.optimized-element.animating {
will-change: transform;
transform: translateZ(0);
}
/* 动画结束后自动清理 */
.optimized-element:not(.animating) {
will-change: auto;
transform: none;
}
注意
过度使用 GPU 加速可能导致内存占用过高,特别是在移动设备上。建议只对确实需要频繁变化的元素启用。
延伸阅读
- 浏览器合成与性能 - GPU 渲染最佳实践
- CSS GPU 动画指南 - 硬件加速动画详解
- Chrome DevTools Layers - 调试合成层工具
如何优化 CSS 来提升性能?
答案
CSS 性能优化主要从减少样式计算复杂度、优化重排重绘、提升渲染效率和加载性能四个维度进行。
样式计算优化
优化策略 | 具体措施 | 性能收益 |
---|---|---|
简化选择器 | 避免深层嵌套、通配符 | 减少样式匹配时间 |
减少样式规则 | 移除未使用CSS、合并重复规则 | 降低计算开销 |
避免复杂属性 | 谨慎使用:nth-child() 、+ 选择器 | 提升匹配效率 |
/* ❌ 性能差的选择器 */
.container > div:nth-child(odd) + div { }
* { box-sizing: border-box; }
/* ✅ 优化后的选择器 */
.list-item-odd + .list-item { }
.box-sizing { box-sizing: border-box; }
重排重绘优化
/* 1. 使用 transform 代替 position 改变 */
.move-element {
/* ❌ 触发重排 */
/* left: 100px; */
/* ✅ 只触发合成 */
transform: translateX(100px);
will-change: transform;
}
/* 2. 避免频繁修改布局属性 */
.optimize-layout {
/* 一次性设置所有布局属性 */
width: 200px;
height: 100px;
margin: 10px;
padding: 5px;
}
/* 3. 使用 contain 属性隔离 */
.isolated-component {
contain: layout style paint;
}
GPU 加速优化
/* 触发硬件加速的属性 */
.gpu-accelerated {
/* 3D 变换 */
transform: translateZ(0);
/* 或者 */
will-change: transform;
/* CSS 滤镜 */
filter: blur(0);
/* 透明度动画 */
opacity: 1;
transition: opacity 0.3s;
}
/* 动画优化 */
@keyframes optimized-animation {
from { transform: scale(1); }
to { transform: scale(1.2); }
}
.animated-element {
animation: optimized-animation 0.3s ease-out;
/* 提前声明变化属性 */
will-change: transform;
}
加载性能优化
<!-- 1. 关键CSS内联 -->
<style>
/* 首屏关键样式 */
.header { background: #000; color: #fff; }
.loading { display: flex; justify-content: center; }
</style>
<!-- 2. 非关键CSS延迟加载 -->
<link rel="preload" href="non-critical.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<!-- 3. 媒体查询优化 -->
<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="mobile.css" media="(max-width: 768px)">
现代CSS特性优化
/* 1. 容器查询减少媒体查询 */
.card-container {
container-type: inline-size;
}
@container (min-width: 300px) {
.card { flex-direction: row; }
}
/* 2. CSS逻辑属性 */
.modern-layout {
margin-inline: 1rem; /* 代替 margin-left/right */
padding-block: 2rem; /* 代替 padding-top/bottom */
}
/* 3. 原生CSS嵌套 */
.component {
color: blue;
&:hover { color: red; }
& .child { font-size: 14px; }
}
实际开发建议
- 性能监控 使用 DevTools Performance 面板分析样式计算耗时
- 构建优化 配置 PurgeCSS 移除未使用的样式
- 分层策略 合理使用
z-index
避免不必要的层级创建 - CSS-in-JS 优化 避免运行时样式计算,优先使用编译时生成
提示
优化优先级:减少重排重绘 > 简化选择器 > 启用GPU加速 > 优化加载策略
延伸阅读
- MDN: CSS性能 - CSS性能最佳实践
- 缩小样式计算的范围并降低其复杂性 google 样式优化相关技巧
- CSS Triggers - 各CSS属性对渲染性能的影响
- contain contain 属性的使用
JS 优化的策略
答案
JavaScript 性能优化主要从减少执行时间、优化内存使用、提升渲染性能和改善用户体验四个维度进行。
代码执行优化
优化策略 | 具体措施 | 性能收益 |
---|---|---|
减少循环复杂度 | 使用高效算法、避免嵌套循环 | 降低时间复杂度 |
缓存计算结果 | 函数记忆化、避免重复计算 | 减少CPU开销 |
延迟执行 | 懒加载、按需执行 | 提升初始加载速度 |
// ❌ 低效的代码
function findUser(users, id) {
for (let i = 0; i < users.length; i++) {
if (users[i].id === id) {
return users[i];
}
}
}
// ✅ 优化后的代码
const userMap = new Map(users.map(user => [user.id, user]));
function findUser(id) {
return userMap.get(id); // O(1) 查找
}
// 函数记忆化
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
内存管理优化
// 1. 避免内存泄漏
class Component {
constructor () {
this.timer = null
this.listeners = []
}
destroy () {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 移除事件监听器
this.listeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler)
})
this.listeners = []
}
}
// 2. 使用 WeakMap 避免强引用
const componentData = new WeakMap()
function setComponentData (component, data) {
componentData.set(component, data)
}
// 3. 对象池复用
class ObjectPool {
constructor (createFn, resetFn) {
this.createFn = createFn
this.resetFn = resetFn
this.pool = []
}
acquire () {
return this.pool.length > 0
? this.pool.pop()
: this.createFn()
}
release (obj) {
this.resetFn(obj)
this.pool.push(obj)
}
}
异步优化策略
// 1. 时间切片处理大量数据
function processLargeData (data, batchSize = 1000) {
return new Promise((resolve) => {
let index = 0
const results = []
function processBatch () {
const endIndex = Math.min(index + batchSize, data.length)
for (let i = index; i < endIndex; i++) {
results.push(processItem(data[i]))
}
index = endIndex
if (index < data.length) {
// 让出主线程,避免阻塞UI
setTimeout(processBatch, 0)
} else {
resolve(results)
}
}
processBatch()
})
}
// 2. Web Worker 处理计算密集任务
function heavyComputation (data) {
return new Promise((resolve, reject) => {
const worker = new Worker('computation-worker.js')
worker.postMessage(data)
worker.onmessage = (e) => {
resolve(e.data)
worker.terminate()
}
worker.onerror = reject
})
}
// 3. requestIdleCallback 优化非关键任务
function scheduleWork (tasks) {
function performWork (deadline) {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
const task = tasks.shift()
task()
}
if (tasks.length > 0) {
requestIdleCallback(performWork)
}
}
requestIdleCallback(performWork)
}
DOM 操作优化
// 1. 批量 DOM 操作
function updateList (items) {
const fragment = document.createDocumentFragment()
items.forEach(item => {
const element = document.createElement('li')
element.textContent = item.text
fragment.appendChild(element)
})
// 一次性插入,减少重排
document.getElementById('list').appendChild(fragment)
}
// 2. 虚拟滚动
class VirtualScroller {
constructor (container, itemHeight, renderItem) {
this.container = container
this.itemHeight = itemHeight
this.renderItem = renderItem
this.visibleStart = 0
this.visibleEnd = 0
this.container.addEventListener('scroll', this.onScroll.bind(this))
}
onScroll () {
const scrollTop = this.container.scrollTop
const containerHeight = this.container.clientHeight
this.visibleStart = Math.floor(scrollTop / this.itemHeight)
this.visibleEnd = Math.min(
this.visibleStart + Math.ceil(containerHeight / this.itemHeight) + 1,
this.data.length
)
this.render()
}
render () {
// 只渲染可见区域的元素
const visibleItems = this.data.slice(this.visibleStart, this.visibleEnd)
// ... 渲染逻辑
}
}
现代优化技术
// 1. ES6+ 优化写法
// 使用解构和扩展运算符
const mergeObjects = (obj1, obj2) => ({ ...obj1, ...obj2 });
// 使用 Map/Set 替代普通对象
const uniqueItems = [...new Set(array)];
// 2. 模块化和树摇
// 按需导入
import { debounce } from 'lodash-es';
// 或使用动态导入
const { debounce } = await import('lodash-es');
// 3. TypeScript 编译优化
// 启用严格模式,帮助编译器优化
// tsconfig.json: "strict": true
实际开发建议
- 性能监控 使用 Performance API 和 DevTools 定位性能瓶颈
- 代码分割 按路由或功能拆分代码,减少初始包体积
- 缓存策略 合理使用浏览器缓存和应用级缓存
- 错误边界 防止单个组件错误影响整个应用性能
提示
优化顺序:识别瓶颈 > 算法优化 > 内存管理 > 异步处理 > DOM优化
延伸阅读
- MDN: JavaScript 性能 - JS性能优化指南
- Web.dev: 优化JavaScript执行 - 执行性能最佳实践
- V8 性能优化 - 引擎层面的优化原理