跳到主要内容

前端性能优化核心点

面试题:你是怎么做性能优化的?

核心四点:

  1. 打开速度怎么变快?—— 首屏加载优化
  2. 再次打开速度怎么变快?—— 缓存
  3. 操作怎么顺滑?—— 渲染优化
  4. 动画怎么流畅?—— 拆分长任务

首屏性能指标

如何衡量加载情况?

指标衡量:

  1. FP(First Paint 首次绘制)
  2. FCP(First Contentful Paint 首次内容绘制),FP 到 FCP 之间存在 SPA 应用的 JS 执行,太慢会导致白屏
  3. FMP(First Meanful Paint 首次有效绘制),主要内容呈现时间
  4. LCP(Largest Contentful Paint 最大内容绘制),加载最大内容块时间

其中 FP、FCP 可以用 Performance 工具检测

FMP 可以使用 MutationObserver 采集

其他提升用户体验的指标:

  1. INP(Interaction to Next Paint)与网页进行的每次点按、点击或键盘互动的延迟时间。
  2. TTI(Time to Interaction 可交互时间)
  3. TBT(Total Blocking Time 总阻塞时间),从 FCP 到 TTI 的时间
  4. CLS(Commulative Layout Shift),布局偏移分数
  5. TTFB(Time to First Byte 首字节到达时间)请求发出后到接收到数据的时间

首屏加载优化

方案

  1. 优化图片:Webp、图片压缩、图片尺寸(在合适的容器中使用合适的图,如 1 倍图、2 倍图、3 倍图)
  2. 字体瘦身:字体瘦身主要是设计型产品,方案有字体子集化

字体子集化(Font subsetting)是指用了哪些字体,最后就只生成对应字的字体文件。

比如使用开源 fontmin 生成字体子集。

  1. 懒加载资源:图片懒加载、JS 异步加载

  2. 精简 CSS 和 JavaScript(打包构建阶段):

    1. 代码压缩:移除空格、注释和多余文字,减少文件大小
    2. 合并文件:将多个 CSS 和 JavaScript 文件合并为一个文件,减少 HTTP 请求次数
    3. Tree Shaking:移除未使用的代码,减少打包文件的体积
  3. CDN(Content Deliver Network):将静态资源托管到 CDN 上,缩短资源

  4. 浏览器缓存:设置适当的缓存策略,使浏览器能够缓存常用的文件,减少重复加载

  5. 压缩文本资源:Gzip 或 Brotli 压缩

  6. SSR、SSG

动画卡顿

为什么会卡顿?单线程

  1. 减少主线程阻塞

    1. 优化 JavaScript 执行,减少长任务(复杂的计算)
    2. 将耗时操作移至 Web Worker
  2. GPU 加速

    1. 使用 CSS 属性(如 transformopacity)触发 GPU 加速
    2. 避免使用 lefttop 等触发重排的属性
  3. 合适的帧率

    1. 确保动画运行在 60 帧/秒(FPS),通过 requestAnimationFrame 控制动画
  4. 压缩动画帧渲染时间

    1. 减少每帧渲染的计算,避免阻塞绘制。
  5. 节流和防抖

    1. 优化滚动时间、窗口调整大小时间,减少不必要的频繁触发。

应用状态管理优化

React 状态管理

  1. 状态局部化,减少全局状态的依赖

    1. 避免使用全局状态(如 Redux 或 Context)管理所有数据
    2. 对于仅在某些组件的状态,可以使用撞见的 useStateuseReducer
  2. 优化 Context 性能,Context 的更新会重新渲染所有订阅的组件

    1. 比如拆分 Context,将不同的逻辑存储在多个 Context 中,降低重新渲染范围
  3. 使用高效的状态管理库

    1. 使用轻量、高性能的状态管理工具,比如 Zustand、Jotai,它们具备更细粒度的状态更新机制
    import create from 'zustand';

    const useStore = create(set => ({
    count: 0,
    increment: () => set(state => ({ count: state.count + 1})),
    }))

    const Counter = () => {
    const count = useStore(state => state.count);
    const increment = useStore(state => state.increment);
    return <button onClick={incremnt}>Count: {count}</button>
    }
  4. 避免不必要的状态更新

    1. 使用不可变数据结构(如 immer)管理状态,减少对数据的直接修改
    import produce from 'immer';

    const nextState = produce(baseState, draft => {
    draft.value = newValue
    })

使用 React Developer Tools 插件,开启「Highlight updates when components render」:

Highlight updates when components render

HIX.ai 的更新情况:

hix ai

飞书的更新情况:

feishu

相比之下,飞书的优化做得更好,更新的颗粒度更小。

Vue 状态管理

  1. 精简 Vuex 或 Pinia 的全局状态
    1. 将不需要全局共享的状态移至组件内部,减少全局状态更新的开销
    2. 示例:使用 reactive 管理局部状态,而不是在全局 store 中存储
  2. 模块化和按需加载
    1. 将 Vuex 或 Pinia 的状态模块化,按需加载,提高性能
  3. 避免多余的 Getter 重计算
    1. 将计算密集型的逻辑放入组件的 computedwatch 中,而不是在 store 的 getter 中

应用视图层更新优化

React 的视图更新优化

  1. 使用 React.memo 防止不必要的重新渲染

    1. 对函数组件进行包裹,只有 props 变化使才重新渲染
    const MyComponent = React.memo(({ data }) => {
    return <div>{ data }</div>
    })
  2. useMemouseCallbck 的优化

    1. 使用 useMemo 缓存复杂计算的结果,使用 useCallback 缓存函数实例
    const computedValue = useMemo(() => heavyComputation(data), [data]);
    const handleClick = useCallback(() => doSomething(), []);
  3. 拆分组件

    1. 将页面拆分为更小的组件,只更新必要的部分,避免整体重新渲染
  4. 使用虚拟滚动

    1. 对于长列表循环,使用虚拟滚动技术(如 React-Virtualized 或 React-Window)只渲染可见区域的内容
  5. 适当使用批处理更新

    1. 确保多个状态变更可以批量处理,减少渲染次数

Vue 的视图更新优化

  1. 避免多余的响应式数据
    1. 只对需要响应式的数据使用 refreactive,静态数据不需要响应式
  2. 使用 v-oncev-memo
    1. 对于不需要更新的静态内容,可以使用 v-once 渲染一次
  3. 拆分组件和局部更新
    1. 将大组件拆分为多个子组件,使用 keep-alive 缓存不活跃的组件,减少重新渲染的开销
  4. 避免 watch 的过度使用
    1. 优化 watch 的逻辑,仅对必要的依赖进行监听,减少副作用执行
  5. 使用虚拟滚动
    1. 对长列表使用虚拟滚动库(如 vue-virtual-scroller)进行优化

事件和渲染细节优化

  1. 节流和防抖
  2. 事件绑定
    1. 在 Vue 中使用 .native 修饰符直接绑定 DOM 事件
    2. 在 React 中,避免在子组件上过多传递回调函数
  3. 避免不必要的 DOM 操作
    1. 减少直接操作 DOM 的次数,尽量通过框架的响应式机制处理更新
  4. 异步加载和懒加载
    1. 对于路由组件、图片等使用懒加载技术,降低首次加载压力
  5. 使用请求合并
    1. 在需要多次请求时,合并请求以减少多余的网络开销

参考

https://www.bilibili.com/video/BV1S2NHeUEtx?vd_source=d5115cd530e622ee8034a1c341f2dff2&p=2&spm_id_from=333.788.player.switch

https://u19tul1sz9g.feishu.cn/docx/NdqqdIX4hoQiIsxR2fecr7ZLnsc?pwd=6&a25855