前端如何处理首屏优化问题

用户普遍反馈项目首屏加载比较满,所以我做了完整的一次性能分析,定位到几个关键的瓶颈;

1.问题主要在js bundle体积过大(3.2mb) 和首屏并发请求过多(12个);

2.bundle问题做了代码分割和懒加载,路由改成动态的import、三方库按需引入。

针对接口就是梳理了下数据依赖,把12个借口合并成2个关键接口。

所以首屏加载时间从4.2秒优化到1.8秒,fcp指标提升了60%,效果显著

如何分析出的这些瓶颈的?

1.performance:

浏览器吧检查调出来有一个performance那一栏,点击录制,然后操作页面,再stop停止录制,就会出现一个火焰图。

发现主线程被大量的js解析任务阻塞(黄色部分占据很大一个比例),同时用Lighthouse跑分,量化出FCP和LCP指标确实比较低。

2.构建分析:

用webpack-buldle-analyzer对打包产物进行可视化分析,一下就可以看出moment.js这种库占了韩大的体积,就改用更轻量的days.js

使用:yarn add --dev webpack-bundle-analyzer

通过 Webpack 配置插件(推荐)

在你的 webpack.config.js(或者 webpack.prod.js等构建配置文件)中引入并使用该插件:

示例代码:

const BundleAnalyzerPlugin =

require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {

// ...你的其他 webpack 配置

plugins: [ // 添加这个插件(不传参数默认会在构建后自动打开分析页面)

new BundleAnalyzerPlugin()

] };

🧠 如何操作:

  • ∙ 运行 webpack 构建后,​​该插件会启动一个本地 HTTP 服务器(通常是 http://localhost:8888)​

  • ∙ 然后​​自动打开浏览器​​,展示打包结果的可视化分析页面

  • ∙ 页面中以​​矩形树图(Treemap)的形式展示各个模块的大小与依赖关系​

如何解决细节

组件方面:

对于一些体积比较大,但是不是一进入页面理解展示的组件 通过动态import()方式进行分割;

代码分离

配置方面:

在webpack配置层面,使用了splitChunks,将常用的第三方库和公共模块抽离成独立的chunk,以便浏览器长期缓存'

将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件

默认情况下,所有的JavaScript代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载,就会影响首页的加载速度

代码分离可以分出出更小的bundle,以及控制资源加载优先级,提供代码的加载性能

这里通过splitChunksPlugin来实现,该插件webpack已经默认安装和集成,只需要配置即可

默认配置中,chunks仅仅针对于异步(async)请求,我们可以设置为initial或者all

复制代码
module.exports = {
    ...
    optimization:{
        splitChunks:{
            chunks:"all"
        }
    }
}

splitChunks主要属性有如下:

  • Chunks,对同步代码还是异步代码进行处理
  • minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分
  • maxSize: 将大于maxSize的包,拆分为不小于minSize的包
  • minChunks:被引入的次数,默认是1

如何保证代码分割后的用户体验

1.我加了一个loading等待的效果,缓解用户的等待焦虑;或者suspense的fallback设计一个骨架屏

import React, { Suspense, lazy } from 'react';

// 动态导入组件 => 会生成单独的 chunk

const HomePage = lazy(() => import('./pages/HomePage'));

function App() {

return (

<Suspense fallback={<div>🌀 页面加载中,请稍候...</div>}>

<HomePage />

</Suspense>

);

}

2.我还弄了一个预加载,就是在用户鼠标hover导航菜单时候,以前与加载对应页面的chunk,让页面条环更快

实现思路:

  • ∙ 不是直接调用 import('./Page')(这会立即加载),而是将动态导入函数保存下来,在 hover 事件里 ​​手动调用它​​。

  • ∙ React.lazy 的 import()是自动执行的,所以我们需要 ​​自行封装动态 import,以拿到 Promise 函数​​,实现"按需手动触发"。

// 不使用 React.lazy,而是自己封装动态加载函数

const getHomePage = () => import('./pages/HomePage');

// 在导航菜单 hover 时预加载

function Navigation() {

const handleMouseEnter = () => {

// hover 时触发 chunk 加载

getHomePage(); // 这里会开始下载对应 chunk

};

return (

<nav>

<span onMouseEnter={handleMouseEnter}>首页</span>

</nav>

);

}

// 真正渲染组件时仍然用 React.lazy + Suspense

const HomePage = React.lazy(getHomePage);

function App() {

return (

<Suspense fallback={<div>加载中...</div>}>

<HomePage />

</Suspense>

);

}

3.用错误边界包裹了动态加载的组件,这样即使整个chunk因为网络问题加载失败,整个应用也不会白屏,给他展示一个toast提示。

除了这些 还有什么其他可以首屏优化的,协同团队做了基础设施层的优化;

1.将所有静态资源部书到cdn的边缘节点,开启http/2协议和gzip压缩

2.和后端同学一起,为核心api增加额redis缓存,并对曼查询的数据库表增加了索引,给接口响应时间从800ms降低到200ms

3。建立监控体系:部署了前端性能监控系统,能够持续追踪core web vitals等核心指标,让优化不再是一次性行为,而是一个长期迭代的体系

相关推荐
WebGirl1 分钟前
实现将html页面导出word (.docx)
前端
眼镜会飞2 分钟前
Flutter 3.x新版android端的build.gradle.kts文件配置arm64-v8a和armeabi-v7a等
android·前端·flutter
用户11481867894847 分钟前
前端实现空闲进程渲染
前端
前端老鹰8 分钟前
JavaScript Object.hasOwn ():更安全的对象属性检测方法
前端·javascript
用户11481867894849 分钟前
https项目中嵌入http格式的iframe, nginx 代理实现
前端
文艺理科生12 分钟前
深入 Nuxt 服务端引擎:用 Nitro 构建全栈应用
前端·javascript·后端
Mr_Wu201819 分钟前
windows electron-builder打包后应用图标更新问题
前端·javascript·electron
Cache技术分享19 分钟前
167. Java Lambda 表达式 - 链接组合比较器
前端·后端
晴转多云54325 分钟前
关于 Vben5 热更新 Cannot read properties of null (reading 'nextSibling')的分析
前端