Skip to content

从“浏览器输入URL”到“最终浏览器出现页面”的过程,讲前端性能优化

常见的性能优化方案

前端性能优化主要围绕两个核心方向:时间(减少耗时)空间(降低资源占用)

  • 时间优化 旨在减少页面加载和交互响应的时间,提高用户体验。 主要在网络耗时页面加载耗时脚本执行耗时渲染耗时等方面优化
  • 空间优化 旨在降低 CPU、内存、本地存储等资源占用,减少页面卡顿甚至崩溃的可能性。主要在 CPU 占用内存占用本地缓存占用等方面优化

时间:减少耗时

我们知道浏览器在页面加载过程中,会进行以下的步骤:

  • 网络请求(发起 HTTP 请求从服务端获取页面资源,包括 HTML/CSS/JS/图片资源等)
  • 浏览器解析 HTML 和渲染页面
  • 加载 Javascript 代码时会暂停页面渲染(包括解析到外部资源,会发起 HTTP 请求获取并加载)

根据这个过程,我们可以从四个方面进行耗时优化:

  1. 网络请求优化。
  2. 首屏加载优化。
  3. 渲染过程优化。
  4. 计算/逻辑运行提速。

在前端性能优化实践中,网络请求优化首屏加载优化 方案使用频率最高,因为不管项目规模如何、各个模块和逻辑是否复杂,这两个方向的耗时优化方案都是比较通用的。 相比之下,对于页面内容较多、交互逻辑/运算逻辑复杂的项目,才需要针对性地进行渲染过程优化和计算/逻辑运行提速。

1. 网络请求优化

网络请求优化的目标在于减少网络资源的请求和加载耗时,如果考虑 HTTP 请求过程,显然我们可以从几个角度来进行优化:

  1. 请求链路:DNS预解析、域名发散、CDN分发、缓存等。
  2. 数据大小:资源压缩,文件拆分。

对于请求链路,核心的方案常常包括 使用缓存,比如 DNS 缓存、CDN 缓存、HTTP 缓存、后台缓存等等,前端的话还可以考虑使用 Service Worker、PWA 等技术。使用缓存并非万能药,很多使用由于缓存的存在,我们在功能更新修复的时候还需要考虑缓存的情况。除此之外,还可以考虑使用 HTTP/2、HTTP/3 等提升资源请求速度,以及对多个请求进行合并,减少通信次数;对请求进行域名拆分,提升并发请求数量。

数据大小则主要考对请求资源进行合理的拆分(CSS、Javascript 脚本、图片/音频/视频等)和压缩,减少请求资源的体积,比如使用 Tree-shaking、代码分割、移除用不上的依赖项等。

在请求资源返回后,浏览器会进行解析和加载,这个过程会影响页面的可见时间,通过对首屏加载的优化,可有效地提升用户体验。

网络请求优化的目标是减少请求次数、降低延迟、压缩数据大小,以提升网页加载速度和用户体验。以下是系统化的网络请求优化策略


1. 减少 HTTP 请求次数

减少 HTTP 请求可以降低服务器负担,加快页面加载速度。

  • CSS & JS 合并

    • 将多个小 CSS/JS 文件合并为一个,减少 HTTP 请求数。
    • 但要注意: 对大型应用,建议使用 按需加载,避免一次性加载所有代码。
  • 图片合并(CSS Sprites)

    • 将多个小图标合并为一张大图,使用 background-position 显示不同部分。
    • 优点: 减少 HTTP 请求,减少服务器压力。
    • 适用于: 背景图标、装饰图片。
  • IconFont 代替小图标


2. 使用缓存(Cache)

缓存的原理就是更快读写的存储介质+减少IO+减少CPU计算=性能优化。而性能优化的第一定律就是:优先考虑使用缓存。
缓存的主要手段有:浏览器缓存、CDN、反向代理、本地缓存、分布式缓存、数据库缓存。

浏览器缓存 可以看另一篇文章: 浅谈 强制缓存/协商缓存 怎么用?文中介绍了浏览器缓存的使用方式以及查看网站当前配置的缓存策略的方法。

浏览器缓存

浏览器缓存可减少重复请求,缓存策略包括:

  • 强缓存(Expires / Cache-Control)

    • Cache-Control: max-age=31536000(一年内不过期)
    • 适用于:静态资源(JS、CSS、图片)。
  • 协商缓存(ETag / Last-Modified)

    • 服务器返回 ETagLast-Modified,浏览器请求时带上 If-None-MatchIf-Modified-Since,服务器决定是否返回 304。
    • 适用于:可更新资源。

> [!tip] 优化策略: > > - CSS、JS 采用 强缓存(Cache-Control,并使用 文件指纹(hash) 版本管理。 > - API 响应数据使用 协商缓存(ETag)

Service Worker(PWA 缓存)

Service Worker 可以拦截网络请求,缓存静态资源,实现离线访问

js
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => response || fetch(event.request))
  );
});
CDN(内容分发网络)缓存

CDN 缓存可减少跨地域请求延迟:

  • 使用 CDN 服务器缓存静态资源(CSS、JS、图片)。
  • CDN 负载均衡,自动选择最优服务器。
Ajax缓存

Ajax在发送的数据成功后,为了提高页面的响应速度和用户体验,会把请求的URL和返回的响应结果保存在缓存内,当下一次调用Ajax发送相同的请求(URL和参数完全相同)时,它就会直接从缓存中拿数据。 在进行Ajax请求的时候,可以选择尽量使用get方法,这样可以使用客户端的缓存,提高请求速度。


3. 减少 DNS 解析时间

DNS 解析会引入额外的时间消耗,可以优化:

3.1 DNS 预解析

浏览器默认会逐步解析域名,启用 DNS 预解析 可以提前解析某些域名:

html
<link rel="dns-prefetch" href="//cdn.example.com">
3.2 连接预建立(Preconnect)

对于关键资源(如 API 服务器、CDN),使用 preconnect 预连接:

html
<link rel="preconnect" href="//api.example.com">
3.3 减少外部资源
  • 减少不同域名的 HTTP 请求,如减少多个 CDN 域名。
  • 避免使用过多第三方库(如广告、统计 SDK)。

4. 使用 HTTP/2 或 HTTP/3

HTTP/2 & HTTP/3 可以减少网络延迟,提升资源加载效率。

4.1 HTTP/2 关键优化
  • 多路复用(Multiplexing):一个 TCP 连接可同时加载多个资源。
  • 头部压缩(HPACK):减少请求体积。
  • 服务器推送(Server Push):预加载用户可能需要的资源。
4.2 HTTP/3(基于 QUIC)
  • 无 TCP 队头阻塞,更快的连接建立(零 RTT)。
  • UDP 传输,更低延迟。

> [!tip] 如何启用? > > - 确保服务器支持 HTTP/2 或 HTTP/3(如 Nginx 配置 listen 443 ssl http2;)。 > - 使用 curl --http3 检测是否支持 HTTP/3。


5. 资源优化

减少传输的数据大小,提高加载效率。

5.1 代码压缩(Minification)
  • JS 压缩Terser / UglifyJS
  • CSS 压缩cssnano
  • HTML 压缩html-minifier
5.2 启用 Gzip / Brotli 压缩

启用 Gzip 或 Brotli 压缩可减少 50% 以上的资源大小:

nginx
server {
    gzip on;
    gzip_types text/plain application/javascript text/css;
}

> [!tip] Brotli > (Google 开发,压缩率比 Gzip 高 20-25%)

nginx
server {
    brotli on;
    brotli_types text/html text/css application/javascript;
}
5.3 图片优化
  • WebP 格式:比 PNG/JPG 小 30% 以上:
html
<picture>
  <source srcset="image.webp" type="image/webp">
  
</picture>
  • 使用 CDN 提供图片优化(如阿里云 OSS、Cloudflare)

6. 负载均衡

当请求量过大时,可以采用负载均衡方案:

  • Nginx 负载均衡
nginx
upstream backend {
    server server1.example.com;
    server server2.example.com;
}
server {
    location / {
        proxy_pass http://backend;
    }
}
  • CDN 负载均衡(如 Cloudflare、阿里云 CDN)

7.域名发散(域名收敛)

就是将同一站点下的静态资源分布在不同域名下。例如:
主站域名www.a.com
访问图片的域名www.a-img.com
访问文件的域名www.a-link.com
详情可以查看另一篇文章:
(域名发散)为什么主页面和静态资源要置于不同的域名下?

2. 页面加载优化

页面加载优化核心点:

  1. 将页面内容尽快地展示给用户,减少页面白屏时间。 减少白屏时间除了我们常说的首屏加载耗时优化,还可以考虑使用一些过渡的动画,让用户感知到页面正在顺利加载,从而避免用户对于白屏页面或是静止页面产生烦躁和困惑。除了技术侧的优化,很多时候产品策略的调整,给用户带来的体验优化效果不低于技术手段优化,因此我们也需要重视。

  2. 将用户可操作的时间尽量提前,避免用户无法操作的卡顿体验。 整体的优化思路包括:尽可能提前页面可见,以及将用户可交互的时间提前。一般来说,我们需要尽可能地降低首屏需要的代码量和执行耗时,可以通过以下方式进行:

  • 对页面的内容进行分片/分屏加载
  • 仅加载需要的资源,通过异步或是懒加载的方式加载剩余资源
  • 使用骨架屏进行预渲染
  • 使用差异化服务,比如读写分离,对于不同场景按需加载所需要的模块
  • 使用服务端直出渲染,减少页面二次请求和渲染的耗时

有些时候,我们的页面也需要在客户端进行展示,此时可充分利用客户端的优势:

  • 配合客户端进行资源预请求和预加载,比如使用预热 Web 容器
  • 配合客户端将资源和数据进行离线,可用于下一次页面的快速渲染
  • 使用秒看技术,通过生成预览图片的方式提前将页面内容提供给用户

首屏加载过程 当用户访问网站时,浏览器会经历以下几个步骤:

  1. 解析 URL,查询缓存
  2. 建立网络连接(DNS 解析、TCP 连接、TLS 认证)。
  3. 发送 HTTP 请求,获取 HTML 文档。
  4. 浏览器解析 HTML,并逐步构建 DOM 树。
  5. 加载并解析 CSS,构建 CSSOM。
  6. 执行 JavaScript,可能会阻塞渲染。
  7. 计算布局(Reflow)
  8. 绘制页面(Repaint)
  9. 最终呈现给用户。

优化目标:减少阻塞渲染的 CSS 和 JavaScript
尽快让首屏可见
尽量缩短首屏可交互时间


1. CSS 优化

CSS 可能会阻塞页面渲染,优化策略:

CSS 关键路径优化
  • 提取关键 CSS(Critical CSS),优先加载首屏样式:
html
<style>
body { background-color: white; }
</style>
  • 使用异步加载 CSS
html
<link rel="stylesheet" href="style.css" media="print" onload="this.onload=null;this.media='all';">
CSS 文件拆分
  • 将首屏关键 CSS 内联,其他 CSS 采用异步加载:
html
<style>
h1 { color: red; }
</style>

2.JavaScript 优化

JS 可能会阻塞 HTML 解析,优化策略:

JS 延迟加载
  • 异步加载 JS
    • async下载后立即执行(可能阻塞渲染)
    • defer下载后,HTML 解析完成再执行(推荐)
html
<script src="script.js" async></script>
<script src="script.js" defer></script>
代码拆分

按需加载 JavaScript,减少首屏 JS 体积:

js
// Vue / React 组件按需加载
const Home = () => import('@/views/Home.vue');
Tree Shaking

移除未使用的代码:

js
import { debounce } from 'lodash-es'; // 仅加载 debounce 方法

3.首屏渲染优化

骨架屏(Skeleton Screen)

骨架屏是一种让用户感知加载状态的方法:

html
<div class="skeleton">
  <div class="skeleton-line"></div>
  <div class="skeleton-box"></div>
</div>
首屏数据预加载

在服务端直接返回首屏数据,减少 API 请求:

html
<script>
  window.__INITIAL_DATA__ = { user: { name: 'John' } };
</script>

4.服务端优化

使用 HTTP/2 或 HTTP/3
  • HTTP/2 多路复用:同时加载多个资源。
  • HTTP/3(QUIC):更低延迟。
服务器端渲染(SSR)

使用 SSR 让首屏 HTML 直接在服务器渲染:

js
// Next.js / Nuxt.js 服务器端渲染
export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return { props: { data } };
}

3. 渲染过程优化

渲染过程优化的核心目标是减少浏览器重绘(Repaint)和回流(Reflow),提升页面流畅度,让用户获得更好的交互体验。

浏览器渲染过程

当浏览器解析 HTML、CSS、JavaScript 时,会经历以下关键步骤:

  1. 解析 HTML,构建 DOM 树
  2. 解析 CSS,构建 CSSOM 树
  3. 合并 DOM 和 CSSOM,生成 Render Tree
  4. 计算 Layout(回流 / Reflow),确定元素的大小和位置。
  5. Painting(重绘 / Repaint),绘制像素到屏幕上。
  6. Composite(合成),GPU 处理不同图层,合成最终画面。

1.减少重绘回流

  • 减少使用 css 属性简写 用border-width, border-style, border-color代替border。因为css简写把所有值初始化为initial,尽量不使用属性简写可以最小化重绘(repaint)和回流(reflow),(实际工作中,由于css简写带来的性能影响微乎其微,再加上css全都展开写还会增加code size,另外css简写还能解决一些样式覆盖的问题,写起来也比较简单快捷,所以实际工作个人怎么顺手怎么来就好)

  • 使用css属性 will-change 提高性能 它的作用是告诉浏览器元素的哪些属性需要修改,使浏览器能够在元素实际更改之前设置优化,通过在实际更改前执行耗时的工作以提升性能(will-change 同样不能滥用,一半在有很大计算量的动画出现性能问题时考虑用 will-change 提高性能)。

  • 通过修改className批量修改元素样式,或者使用Tailwind等css预处理器;

  • 复杂的动画元素定位要设置为 fixedabsoult,避免引起回流;

  • 不使用table布局(table元素一旦触发回流就会导致table里所有的其它元素回流);

  • DOM 元素上下移动用translate替代top修改;

  • 需要创建多个DOM节点时,使用DocumentFragment一次性创建。

  • 元素适当地定义高度或最小高度 否则元素的动态内容载入时,会出现页面元素的晃动或位置,造成回流(比如图片要定义宽高,避免页面塌陷,同时减少回流);

  • 选择器上 减少使用层级较深的选择器,或其他一些复杂的选择器,以提高CSS渲染效率;

  • 在大量修改元素样式时,可以先用display: none将其隐藏,修改完再设置为display: block,这样只会造成两次回流;

  • 必要时使用css属性 contain 将一个元素及其内容与文档流隔离,防止元素内部在其包围盒外产生副作用。

💀 1.避免不必要的回流(Reflow)

回流(Reflow)发生时,浏览器需要重新计算页面布局,调整元素的大小和位置,是最昂贵的渲染操作。

  • 触发回流的操作:

    • 改变元素的尺寸(widthheightpaddingmargin)。
    • 改变文档结构(插入/删除 DOM 元素)。
    • 计算 offsetWidthoffsetHeightgetBoundingClientRect() 等属性。
  • 合并多次 DOM 操作

    • 错误做法(多次操作 DOM):
js
const el = document.getElementById('box');
el.style.width = '100px';
el.style.height = '100px';
el.style.margin = '10px';
  • 优化做法(使用 CSS 类替代)
js
el.classList.add('new-style');
css
.new-style {
  width: 100px;
  height: 100px;
  margin: 10px;
}
  • 使用 documentFragment 一次性插入多个节点

    js
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100; i++) {
      const div = document.createElement('div');
      fragment.appendChild(div);
    }
    document.body.appendChild(fragment);
  • 避免频繁读取和修改元素的几何信息

    • 错误做法(会导致回流)
js
for (let i = 0; i < 100; i++) {
  let width = el.offsetWidth; // 触发回流
  el.style.width = width + 1 + 'px';
}
  • 优化做法(先读取,再批量修改)
js
const width = el.offsetWidth;
for (let i = 0; i < 100; i++) {
  el.style.width = width + 1 + 'px';
}
🎨2. 减少重绘

重绘(Repaint)发生时,元素的视觉外观发生变化(如颜色、背景),但位置和大小不变,开销小于回流。

  • 触发重绘的操作:

    • 改变 colorbackground-colorbox-shadow
    • 修改 visibility(但 display: none 会触发回流)。
  • 尽量使用 visibility: hidden 代替 display: none

css
/* 错误:display: none 触发回流 */
.hide { display: none; }

/* 优化:visibility 仅触发重绘 */
.hide { visibility: hidden; }
  • 避免使用 box-shadowborder-radiusbackground-image,这些都会导致重绘。

  • 将复杂动画放到 GPU 加速层

🚀 2. 利用 GPU 加速

浏览器针对处理 CSS 动画进行了优化,可以很好地处理触发重排(因此也导致重绘)的动画属性。为了提高性能,可以将设置了动画的节点从主线程移到 GPU 上。 可以触发 GPU 硬件加速的css属性有:transform、filter、will-change、position: fixed等,只要元素被提升为图层,动画就会在 GPU 中完成,从而改善性能,尤其是在移动设备上(但是不能滥用,会导致性能问题)。

  • 开启硬件加速
css
.animate {
  transform: translateZ(0);
  will-change: transform;
}
  • 使用 transform 替代 top/left 进行动画
css
/* ⚠️ 使用 left/top 会触发回流 */
.move {
  position: absolute;
  left: 100px;
}

/* ✅ 使用 transform 避免回流 */
.move {
  transform: translateX(100px);
}
  • 使用 opacity 进行渐变动画
css
/* ✅ opacity 仅触发重绘 */
.fade {
  transition: opacity 0.5s ease-in-out;
}

📦 3. 懒加载和预加载

懒加载(Lazy Loading) 简言之就是只有当图片出现在浏览器的可视区域内时,才加载图片让图片显示出来(在此之前可以将所有图片元素的路径全都统一设置成一张1*1px的占位图)。
判断图片出现在浏览器可视区域的方法:图片距离顶部的高度(offsetTop) - 页面被卷去的高度(scrollTop) 〈= 浏览器的可视区域的高度(innerHeight)

  • 对于图片
html
  • 使用 IntersectionObserver 懒加载
js
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src;
      observer.unobserve(entry.target);
    }
  });
});
document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));

预加载(Preload) Resource Hints(资源预加载)包括预连接、资源与获取、资源预渲染等。
预加载的思路有如下两个:

  1. 当前将要获取资源的列表;
  2. 通过当前页面或应用的状态、用户历史行为或 session 预测用户行为及必需的资源. 实现Resource Hints的方法有很多种,可分为基于 link 标签的 DNS-prefetch、subresource、preload、 prefetch、preconnect、prerender,和本地存储 localStorage。详情可查看: 预加载5种不同的方法3种Javascript图片预加载的方法详解
  • 对于关键资源
html
<link rel="preload" href="main.js" as="script">

🌐 4. 服务器端渲染(SSR)

SSR(Server Side Rendering),即服务端渲染。它指的是渲染过程在服务端完成,最终的渲染结果 HTML 页面通过 HTTP 协议发送给客户端,又叫“同构“。

SSR主要带来的好处就是 SEO 和首屏加载速度大大提高。
目前流行的 Vue/React 前端框架,都已经推出了SSR的解决方案:
Vue的nuxt.js
React的next.js
以前的一篇文章现有Vue项目改造SSR(简单上手)

js
export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return { props: { data } };
}

适用于:

  • SEO 友好的应用(如电商、博客)。
  • 需要快速首屏渲染的页面。

4. 计算/逻辑运行提速

计算/逻辑运行速度优化的主要思路是“拆大为小、多路并行”,方式包括但不限于:

  • 通过将 Javscript 大任务进行拆解,结合异步任务的管理,避免出现长时间计算导致页面卡顿的情况
  • 将耗时长且非关键逻辑的计算拆离,比如使用 Web Worker
  • 通过使用运行效率更高的方式,减少计算耗时,比如使用 Webassembly
  • 通过将计算过程提前,减少计算等待时长,比如使用 AOT 技术
  • 通过使用更优的算法或是存储结构,提升计算效率,比如 VSCode 使用红黑树优化文本缓冲区的计算
  • 通过将计算结果缓存的方式,减少运算次数

1.Web Worker 多线程计算

> [!tip] > Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

在后台执行计算,避免主线程卡顿

js
const worker = new Worker('worker.js');
worker.postMessage('data');
worker.onmessage = (event) => console.log(event.data);

合理实用web worker可以优化复杂计算任务。这里直接抛阮一峰的入门文章:传送门

2.减少不必要的计算

  • 避免深层递归,使用循环优化
  • 缓存计算结果
js
function memoize(fn) {
  let cache = {};
  return function (key) {
    if (cache[key]) return cache[key];
    return (cache[key] = fn(key));
  };
}

空间:降低资源占用

提到性能优化,大多数我们都在针对页面加载耗时进行优化,对资源占用的优化会更少,因为资源占用常常会直接受到用户设备性能和适应场景的影响,大多数情况下优化效果会比耗时优化局限,因此这里也只能说一些大概的思路。

资源占用常见的优化方式包括:

  • 合理使用缓存,不滥用用户的缓存资源(比如浏览器缓存、IndexDB),及时进行缓存清理
  • 避免存在内存泄露,比如尽量避免全局变量的使用、及时解除引用等
  • 避免复杂/异常的递归调用,导致调用栈的溢出
  • 通过使用数据结构享元的方式,减少对象的创建,从而减少内存占用

说到底,我们在做性能优化的时候,其实很多情况下会依赖时间换空间、空间换时间等方式。性能优化没有银弹,只能根据自己项目的实际情况做出取舍,选择相对合适的一种方案去进行优化。

对于页面耗时和资源占用的性能优化分析,大部分情况都可以使用 Chrome 开发者工具进行针对性的分析和优化。

1.打包优化

1. webpack优化resolve.alias配置(vite同理)

resolve.alias 配置通过别名来将原导入路径映射成一个新的导入路径。
可以起到两个作用: 7. 起别名 8. 减少查找过程

例如:

json
resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },

2. webpack优化resolve.extensions配置(vite同理)

resolve.extensions 代表后缀尝试列表,它也会影响构建的性能,默认是:
extensions: ['.js', '.json']
例如遇到require('./data')这样的导入语句时,Webpack会先去寻找./data.js文件,如果该文件不存在就去寻找./data.json文件,如果还是找不到就报错。
【所以后缀尝试列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中,频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。】
在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。例如在你确定的情况下把require('./data')写成require('./data.json')

json
resolve: {
    extensions: ['.js', '.vue', '.json'],
}

3. webpack缩小loader范围

loader 是很消耗性能的一个点,我们在配置 loader 的时候,可以使用 includeexcept 来缩小loader 执行范围,从而优化性能。 例如:

json
{
    test: /\.svg$/,
    loader: 'svg-sprite-loader',
    include: [resolve('src/icons')]
 },

4. split chunks代码分包

在没配置任何东西的情况下,webpack 4 就智能的帮你做了代码分包。入口文件依赖的文件都被打包进了main.js,那些大于 30kb 的第三方包,如:echarts、xlsx、dropzone等都被单独打包成了一个个独立 bundle。 其它被我们设置了异步加载的页面或者组件变成了一个个chunk,也就是被打包成独立的bundle。 它内置的代码分割策略是这样的:

新的 chunk 是否被共享或者是来自 node_modules 的模块;
新的 chunk 体积在压缩之前是否大于 30kb;
按需加载 chunk 的并发请求数量小于等于 5 个;
页面初始加载时的并发请求数量小于等于 3 个;

大家可以根据自己的项目环境来更改配置。webpack配置代码如下:

json
splitChunks({
  cacheGroups: {
    vendors: {
      name: `chunk-vendors`,
      test: /[\\/]node_modules[\\/]/,
      priority: -10,
      chunks: 'initial',
    },
    dll: {
      name: `chunk-dll`,
      test: /[\\/]bizcharts|[\\/]\@antv[\\/]data-set/,
      priority: 15,
      chunks: 'all',
      reuseExistingChunk: true
    },
    common: {
      name: `chunk-common`,
      minChunks: 2,
      priority: -20,
      chunks: 'all',
      reuseExistingChunk: true
    },
  }
})

没有使用webpack4.x版本的项目,可以通过按需加载的形式进行分包:
webpack如何使用按需加载

vite配置代码如下:

json
 build: {
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',  // 引入文件名的名称
        entryFileNames: 'js/[name]-[hash].js',  // 包的入口文件名称
        assetFileNames: '[ext]/[name]-[hash].[ext]' // 资源文件像 字体,图片等
      }
    }
 }

5. tree shaking

Tree Shaking 是一种 消除未使用代码(Dead Code Elimination) 的优化技术,它会在构建时移除未被引用的代码,以减少 JavaScript 体积。

  • 适用于 ES Module(ESM),因为 ESM 具有静态分析能力,能够在构建时知道哪些模块未被使用:
  • 适用于 Webpack、Rollup、Vite 等打包工具,默认支持 Tree Shaking,但前提是代码是 ESM 格式

它依赖于ES Module(ESM)中的模块语法得以实现。 比如日常使用lodash的时候:

json
import _ from 'lodash'

如果如上引用lodash库,在构建包的时候是会把整个lodash包打入到我们的bundle包中的。

js
import _isEmpty from 'lodash/isEmpty';

如果如上引用lodash库,在构建包的时候只会把isEmpty这个方法抽离出来再打入到我们的bundle包中。

tree shaking可以大大减少包体积,是性能优化中的重要一环。 在 vite 和 webpack4.x 中都已经默认开启tree-shaking。

6. vite关闭一些打包配置项()

webpack也有类似的配置,自行查阅

json
build: { 
      terserOptions: {
        compress: {
          //生产环境时移除console
          drop_console: true,
          drop_debugger: true,
        },
      },
      //关闭文件计算
      reportCompressedSize: false,
      //关闭生成map文件 可以达到缩小打包体积
      sourcemap: false, // 这个生产环境一定要关闭,不然打包的产物会很大
}

2.文件优化

1. 组件按需引入

组件按需引入(Component On-Demand Import) 是指只引入需要使用的 UI 组件,而不是整个组件库,它减少了初始 JavaScript 体积

  • 适用于 UI 组件库(Element UI、Ant Design、Vant、Material UI 等)
  • 适用于 Vue、React 等前端框架中的动态组件加载

例如

js
import { Button } from 'ant-design-vue';

2. 图片优化

  • 图片压缩: 开发中比较重要的一个环节,现在很多图床工具都自带压缩功能的。如果需要自己压缩的话,可以在tinyPng 网站压缩一下,一看就会;

  • 图片分割: 如果页面需要加载一张效果图,比如真机渲染图,UI设计师不允许压缩的时候,我们就可以将图片分割,然后再用css布局将图片拼接到一起。

  • 精灵图: 与图片分割相反,精灵图是将很多小图片合并到一张大图里,这样加载页面的时候,只需要加载一张图片,就可以加载出来所有的页面元素。这样在一定程度上提高了页面加载速度。而我们使用精灵图的方式,也是通过定位,即通过background-position来移动背景图,从而显示出我们想要显示出来的部分。
    但是精灵兔有个很大的不足,那就是牵一发而动全身,因为我们使用精灵图需要准确测量每个元素的位置,一旦要调整页面,将会是一项很麻烦的工作。

3.字体包压缩

使用字体子集,减少未使用的字符

  • 问题描述: 在做类似活动h5的时候,难免需要使用 @font-face 接口引入一些字体包,以达到丰富的页面效果。但是完整的字体包文件一般都很好几兆,加载页面时,不仅会阻塞页面渲染,还会导致文字开始不会显示,直至字体包加载完才会显示文字。

  • 解决方式: 可以使用font-spider字蛛将要使用到的文字提取出来。


前端性能优化分类归纳表

优化维度优化分类优化策略
🕒 时间优化网络请求优化减少 HTTP 请求数(资源合并、精灵图、减少第三方库) <br>✅ 使用缓存(浏览器缓存、CDN 缓存、Service Worker) <br>✅ DNS 预解析dns-prefetchpreconnect) <br>✅ 使用 HTTP/2 / HTTP/3(多路复用、头部压缩) <br>✅ 数据压缩(Gzip / Brotli、WebP)
首屏加载优化资源加载优化(代码压缩、Gzip/Brotli 压缩、Preload) <br>✅ CSS 关键路径优化(提取关键 CSS、异步加载非关键 CSS) <br>✅ JS 加载优化async / defer、代码拆分、Tree Shaking) <br>✅ 图片优化(懒加载、骨架屏) <br>✅ 服务器端渲染(SSR)(首屏 HTML 直出)
渲染过程优化减少回流(Reflow)(批量 DOM 操作、CSS 变更合并) <br>✅ 减少重绘(Repaint)visibility: hidden 代替 display: none) <br>✅ 使用 GPU 加速transform 代替 top/left、硬件加速) <br>✅ 懒加载 & 预加载(IntersectionObserver、Preload)
计算/运行提速Web Worker 多线程计算(后台计算,避免主线程卡顿) <br>✅ 减少不必要的计算(缓存计算结果、优化递归) <br>✅ 优化 JavaScript 执行(减少 setTimeout、避免深层嵌套)
🗄 空间优化打包优化Tree Shaking(移除未使用代码) <br>✅ 代码分割(Code Splitting)(按需加载组件,减少主包体积) <br>✅ 优化 Webpack/Vite 配置(alias、resolve、关闭 SourceMap)
文件优化组件按需引入(避免全量引入 UI 组件库) <br>✅ 图片优化(使用 WebP、CDN 处理图片) <br>✅ 字体优化(使用字体子集,减少不必要的字符)

参考:

评论区

欢迎留言、补充或勘误。

xiaoba.blog