Skip to content
大纲

前端工程

项目优化

加载优化

不同于传统 GUI 开发应用先下载后使用的方式,网页应用是动态增量下载的
因而页面加载是对用户使用体验影响最大的部分,超过 3s 的加载时间会导致大量用户的流失。
提高加载速度,即在最短的时间内获取用户需要使用到的资源,包括对资源的压缩和控制资源的获取两大方面。

资源方面

图片资源

  • 小图资源:对于常规小图采用 BASE64 编码,对于图标资源转为字体图标或 svg 图标,以减少资源请求
  • 大图资源:开启资源压缩,或使用预览图片用于原图加载的缓冲

代码资源

  • 使用 tree shaking 移除无效代码
  • 开启压缩减少代码体积
  • 使用 SplitChunks 拆分依赖

网络方面

配置 CDN

  • 对静态资源或静态页面开启 CDN
  • 针对不同的资源类型使用不同的域名(并避免 cookie 的携带)

配置缓存

  • 频繁修改的资源:使用协商缓存,配置 Cache-Control: no-cache 并配合 Etag 检测是否使用缓存
  • 长期不变的资源:使用强缓存,配置 Cache-Control: max-age=xxx
  • 无需缓存的资源:禁用缓存,配置 Cache-Control: no-store

开启 HTTP2

  • 多路复用:提高 TCP 连接使用效率
  • 头部压缩:减少数据请求大小

服务端

  • DNS 解析使用 IP 循环
  • 开启反向代理实现负载均衡
  • 开启 GZIP 压缩减少响应体积

加载方面

标签位置

  • 样式文件:置于页面头部,CSS 文件的加载不会阻塞 DOM 的解析
  • 脚本文件:置于页面底部,JS 文件的加载会阻塞页面的解析和渲染

预处理

  • DNS 预获取:<link rel="dns-prefetch" href="//www.xxx.com" />
  • 资源预加载:<link rel="preload" href="https://www.xxx.com" as="style" />
    • 用于预先加载当前页面需要使用的资源,提高在后续使用时的效率
  • 资源预获取:<link rel="prefetch" href="https://www.xxx.com" />
    • 用于预先获取接下来需要使用的资源并缓存 5 分钟,提高页面切换时的效率
  • 资源预渲染:<link rel="prerender" href="https://www.xxx.com" />
    • 实验中的属性,预先获取资源并在屏幕之外进行渲染,提高渲染时的效率

懒加载

  • 懒加载:在使用时再加载
    • 路由切换:常见于 SPA 应用
    • 滚动加载:当模块滚动到可视区域时进行加载(如 <img loading="lazy" />)
  • 懒执行:在使用时再执行,通常用于耗时逻辑,一般通过事件唤醒

首屏加载

首屏加载主要指优化白屏时间以提升用户体验

  • 对于需要服务请求的页面:可以使用 loading 或骨架屏缓解用户等待体验
  • 对于无需服务请求的页面:可以使用服务端渲染或静态页面

运行优化

某些时候,可能需要使用动画效果提升页面表现,或者渲染大量的列表元素用于内容展示
前者往往容易频繁的触发页面的回流重绘降低网页流畅度,后者可能导致页面短时间卡顿。

渲染优化

  • 独立图层:独立图层的布局互不干扰,可以提高渲染效率
    • 有 3d transform
    • opacitytransformfilter 应用了动画或过渡
    • 设置 will-change: opacity|transform|top|left|right|bottom
  • 减少回流
    • 使用 transformopacity 实现动画效果,会创建独立图层并获得 GPU 加速
    • 使用 requestAnimationFrame 实现动画
    • 对于多次操作的 DOM 元素先离线(display: none),等处理完毕后再展示

批量渲染

对于一次性渲染大量元素,可以使用 requestAnimationFrame 控制渲染频率,使用 DocumentFragment 对多个元素进行一次插入

js
// <div id="wrap"></div>
function render() {
  const total = 1e4;
  const once = 1e2;
  let nums = 0;
  function append() {
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < once; i++) {
      const div = document.createElement("div");
      div.innerText = Math.random();
      fragment.appendChild(div);
    }
    wrap.appendChild(fragment);
    nums += once;
    if (nums < total) {
      requestAnimationFrame(append);
    }
  }
  append();
}
render();

虚拟渲染

对于大量数据渲染的列表,若每项高度可计算,可以仅渲染可视区域以及滚动缓冲部分,对于其他不可视部分,使用一个计算后的空白盒子进行支撑。

计算优化

使用 web worker 将耗时的操作交给其他线程,减轻主线程压力