跳到主要内容

渲染和运行时✅

如何优化 DOM 来提升性能?

答案

DOM 对性能的影响主要体现在

  1. 大量的 DOM 节点,导致首帧渲染耗时变长,因为需要更多的时间来进行布局和绘制。
  2. DOM 树的大批量修改,导致的重排(Reflow)和重绘(Repaint),会影响页面的渲染性能。

大量 DOM 节点的问题典型思路如下

  1. 减少 DOM 节点数量:通过异步加载组件、懒加载等方式,减少初始渲染时的 DOM 节点数量。
  2. 使用虚拟 DOM:如 React、Vue 等框架,通过虚拟 DOM 技术,减少实际 DOM 操作的次数,提高渲染性能。
  3. content-visibility 属性,可以让浏览器在元素不可见时跳过其渲染,提高性能。

对于 DOM 树的批量修改,典型思路如下

  1. 批量更新:将多次 DOM 操作合并为一次操作,减少重排和重绘的次数。比如 react batch 机制等
  2. 使用 DocumentFragment:在内存中构建 DOM 结构,最后一次性插入到文档中,减少对实际 DOM 的操作次数。
  3. 使用 CSS 动画:通过 CSS 动画代替 JavaScript 动画,减少 JavaScript 线程的压力,提高性能。
  4. 使用 requestAnimationFrame 通过闲时操作避免阻塞渲染,减少对主线程的影响。

延伸阅读

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 加速可能导致内存占用过高,特别是在移动设备上。建议只对确实需要频繁变化的元素启用。

延伸阅读

如何优化 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加速 > 优化加载策略

延伸阅读

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优化

延伸阅读

48%