通过这些case,我把项目LCP时间减少了1.5s

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

前言

最近在做公司几个项目性能优化,整理出一些比较有用且常见的case来分享一下。

A项目优化

白屏相关

DNS预连接、资源预解析

对于公共域名g.alicdn.cmon,DNS预请求:

<link rel="preconnect" href="//g.alicdn.com" crossorigin />
<link rel="dns-prefetch" href="//g.alicdn.com" />

对于一些资源,资源预加载:

<link rel="preload" href="https://g.alicdn.com/eleme-risk/chuangdao-pc/0.0.99/js/index.js" as="script" />
<link rel="preload" href="//g.alicdn.com/alilog/mlog/aplus_v2.js" as="script" />

结果:白屏时间减少400~600ms左右。

页面级路由懒加载

原本创道打包出来的JS文件只有一个bundle.js,涵盖了整个项目的业务代码,对于城市CM来说,可能访问最多的就是新增定向看和任务详情两个页面,所以对于首屏加载是不友好的,应该优化成访问哪个页面加载对应页面的资源,基于Ice2.0调研,将路由中的组件都转换为懒加载模式:

  1. routes.ts
import { lazy, IRouterConfig } from 'ice';
// ice不支持layout组件设置为懒加载
import Layout from '@/layouts/BasicLayout';const Home = lazy(() => import(/* webpackChunkName: 'Home' */ '@/pages/Home'));
const NotFound = lazy(() => import(/* webpackChunkName: 'NotFound' */ '@/components/NotFound'));
const ManualDetect = lazy(() => import(/* webpackChunkName: 'ManualDetect' */ '@/pages/ManualDetect'));
const AddMission = lazy(() => import(/* webpackChunkName: 'addMission' */ '@/pages/ReconnaissanceMission/add-mission'));
const MissionDetail = lazy(() => import(/* webpackChunkName: 'missionDetail' */ '@/pages/ReconnaissanceMission/missionDetail'),
);
const NewMissionDetail = lazy(() => import(/* webpackChunkName: 'newMissionDetail' */ '@/pages/ReconnaissanceMission/newMissionDetail'),
);
const NoPermission = lazy(() => import(/* webpackChunkName: 'NoPermission' */ '@/pages/NoPermission'));
const Board = lazy(() => import(/* webpackChunkName: 'Board' */ '@/pages/Board'));
const BusinessInsight = lazy(() => import(/* webpackChunkName: 'BusinessInsight' */ '@/pages/BusinessInsight'));
const ChuangDaoInsight = lazy(() => import(/* webpackChunkName: 'ChuangDaoInsight' */ '@/pages/ChuangDaoInsight'));
const Report = lazy(() => import(/* webpackChunkName: 'Report' */ '@/pages/Report'));const routes: IRouterConfig[] = [{path: '/',component: Layout,children: [{path: '/manualDetect',component: ManualDetect,},{path: '/addMission',component: AddMission,},{path: '/MissionDetail',component: MissionDetail,},{path: '/newMissionDetail',component: NewMissionDetail,},{path: '/',exact: true,component: Home,},{path: '/noPermission',exact: true,component: NoPermission,},{path: '/board',exact: true,component: Board,},{path: '/businessInsight',exact: true,component: BusinessInsight,},{path: '/chuangDaoInsight',exact: true,component: ChuangDaoInsight,},{path: '/report',exact: true,component: Report,},{component: NotFound,},],},
];export default routes;

2.build.json

{// ..."router": {"lazy": true}
}

线上效果:

首屏在A页面:

image.png

只请求了对应A页面的代码,JS文件大小12.7KB,再进入到立即检查页面:

image.png

继续请求了对应跳转新页面的代码,文件大小也是KB量级的,再看一下优化前的首屏请求情况,无论访问哪个页面,请求的资源是一样的。

image.png

image.png

结果:白屏时间整体降低,请求资源大小整体下降。

构建相关

优化本地热更新时间

创道的本地热更新时间比较慢,大约在8~9秒,基于ice运行时中间件在每次代码变更时加入缓存同时移除对node_module目录下的babel转换。

module.exports = ({ onGetWebpackConfig }) => {onGetWebpackConfig((config) => {config.module.rule('tsx').test(/.jsx?|.tsx?$/).exclude.add(/node_modules/).end().use('babel-loader').tap((options) => {return {...options,cacheDirectory: true,};});});
};

在build.json中注入该插件:

{// ..."plugins": ["@ali/build-plugin-faas",["build-plugin-ignore-style",{"libraryName": "antd"}],"@ali/build-plugin-ice-def","./src/index.ts"]
}

结果:热更新时间降低到4秒左右,降低50%。

构建包大小优化

CDN资源替代项目依赖包

通过webpack可视化工具可以看到创道PC端的一些依赖包体积偏大,影响了页面渲染的时间:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwSv8GVh-1691738939680)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/804fde1f2a0d403fac6b817aaff70598~tplv-k3u1fbpfcp-zoom-1.image)]

从上图可以看到:在开发环境整个构建包体积达到了19.44MB,echarts、antv、moment这些包,体积都比较大,达到了MB量级,并且在项目中前两者使用频率很低,只有引用过一次,对于这种情况,考虑将依赖包转换为CDN引入的方式,原因如下:

  • 减少打包产物大小;
  • 减少白屏时间;
  • 版本固定,使用频率低,通过CDN单独引入还会有浏览器强缓存的效益;

解决方案:

通过webpack中externals,解绑对于node_modules中枚举包的编译,并且在项目index.html中从CDN引入所列举到的包。

{// ..."externals": {"echarts": "echarts","moment": "moment"},
}

这里的key,value值分别对应npm中的包名和CDN引入后在window下的全局变量名,找包的CDN路径很简单,但是如何知道全局变量名是什么呢?

可以打开CDN链接,格式化代码,大概是这个样子的:

function(e, t) {"object" == typeof exports && "object" == typeof module ? //判断环境是否支持commonjs模块规范module.exports = t(require("vue")) :"function" == typeof define && define.amd ? //判断环境是否支持AMD模块规范define("ELEMENT", ["vue"], t) :"object" == typeof exports ? //判断环境是否支持CMD模块规范exports.ELEMENT = t(require("vue")) : e.ELEMENT = t(e.Vue)
} ("undefined" != typeof self ? self: this,function(e){//省略...
});

只需要看一下立即执行函数向外暴露的变量名是什么即可。

代码分割

对于项目中多次引用到的包和公共模块,开启webpack代码分割模式,这部分代码写在之前定义的运行时中间件中:

module.exports = ({ onGetWebpackConfig }) => {onGetWebpackConfig((config) => {config.optimization.splitChunks({cacheGroups: {vendor: {priority: 1,test: /node_modules/,chunks: 'initial',minChunks: 1,minSize: 0,name: 'vendor',filename: 'vendor.js',},common: {chunks: 'initial',name: 'common',minSize: 100,minChunks: 3,filename: 'common.js',},},});});
};

抽离出来的vendor.js模块如图:

结果:优化后的构建包体积为9.1MB,降低了50%以上大小。

目前对于创道H5做了如下优化内容:

B项目优化

白屏相关

HTML文件脚本加载改造

由于JS是单线程,脚本加载会直接阻塞页面渲染,因此对于一些直接放在HTML模板中并且优先级较低的JS文件,在创道H5中例如aplus埋点、vconsole判断加载包、exlog性能监控等与用户体感上无关的脚本文件直接异步加载解析即可:

<script defer>try {const isXuanYuan = /AliApp(EVE//i.test(navigator.userAgent);if (isXuanYuan) {document.documentElement.setAttribute('data-theme', 'xy')} else {document.documentElement.setAttribute('data-theme', 'default')}} catch (e) {}</script>
<script defer>(function (w, d, s, q, i) {w[q] = w[q] || [];var f = d.getElementsByTagName(s)[0],j = d.createElement(s);j.async = true;j.id = 'beacon-aplus';j.setAttribute('exparams', 'clog=o&aplus&sidx=aplusSidx&ckx=aplusCkx');j.src = 'https://g.alicdn.com/alilog/mlog/aplus_wap.js';f.parentNode.insertBefore(j, f);})(window, document, 'script', 'aplus_queue');</script>
<script defer>const domain = location.hostname;let env = 'prod';if (/.(((alibaba|taobao|tmall).net)|daily.elenet.me)$/.test(domain) || location.hostname === 'local.ele.me') {// 集团&饿了么 DAILYenv = 'dev';} else if (/^(pre|ppe)-\w+./.test(domain)) {// 集团&饿了么 PREenv = 'pre';}console.log('env:', env)const debug = {dev: true,pre: false,prod: false,} [env];window._ex = {biz: 'a2fe9.26877649', // 配置spm或业务标识bizType: 'KOUBEI', // 固定入参,不要更改debug: debug, // 开启后会在console打印日志,但注意不会上报,仅用于调试enableOutsidePerformance: true, // 端外上报性能需要开启commonParams: {}, // 添加全局参数,也可以直接赋值到window.ExLog.commonParams = {}whiteScreen: true, // 是否开启白屏监控};// if (env !== 'prod') {//   eruda.init();// }if (env !== 'prod') {const vConsoleScript = document.createElement('script');vConsoleScript.src = 'https://cdn.bootcdn.net/ajax/libs/vConsole/3.9.1/vconsole.min.js';document.body.appendChild(vConsoleScript);vConsoleScript.onload = () => {var vConsole = new VConsole();}}
</script>
<script src="https://gw.alipayobjects.com/as/g/koubei-data-center/exlog/1.4.1/index.js" defer></script>

优化前:

资源加载的并发性偏低,也直接影响到了云鼎接口的调用时机,平均在1400ms的时候才会调(走到useEffect),LCP平均为1300ms。

优化后:

资源加载的并发度提高了很多,并且平均在1100ms的时候就会开始调云鼎,LCP平均为1000ms,提升了300ms。

DNS预请求、资源预解析

由于项目使用umi,开发创道本身umi plugin给head插入一些link标签从而进行优化:

/plugins/preloadPlugin.ts

import type { IApi } from 'umi';export default (api: IApi) => {api.addHTMLLinks(() => {return [{href: '//shadow.elemecdn.com',rel: 'dns-prefetch',},{href: '//g.alicdn.com',rel: 'dns-prefetch',},{href: '//gw.alipayobjects.com',rel: 'dns-prefetch',},{href: '//render.alipay.com',rel: 'dns-prefetch',},{href: 'https://shadow.elemecdn.com/faas/chuangdao-h5-fe-gray/umi.c166c725.js',rel: 'preload',as: 'script',},{href: 'https://shadow.elemecdn.com/faas/chuangdao-h5-fe-gray/layouts__BasicLayout.e2bc9944.async.js',rel: 'preload',as: 'script',},{href: 'https://shadow.elemecdn.com/faas/chuangdao-h5-fe-gray/wrappers.b5ead63e.async.js',rel: 'preload',as: 'script',},];});
};

.umirc.ts中加入该插件

import { defineConfig } from 'umi';export default defineConfig({// ...plugins: [require.resolve('./src/plugins/preloadPlugin.ts')],
});

优化前:

FP/FCP与LCP跨度较大,js资源请求比较分散

优化后:

LCP快了500ms左右,同时js资源请求并发度高了,重复利用起来了。

结尾

本文记录了博主工作中实际优化到的一些实用case,优化是灵活的,需要根据自己的场景来定,对你有帮助那就最好不过啦。

如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/85445.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数学建模】--聚类模型

聚类模型的定义&#xff1a; “物以类聚&#xff0c;人以群分”&#xff0c;所谓的聚类&#xff0c;就是将样本划分为由类似的对象组成的多个类的过程。聚类后&#xff0c;我们可以更加准确的在每个类中单独使用统计模型进行估计&#xff0c;分析或预测&#xff1b;也可以探究不…

【JavaSE】数组的定义与使用

详解数组 数组的基本概念什么是数组数组的创建及初始化数组的使用 数组是引用类型基本类型变量与引用类型变量的区别引用变量认识 null 数组的应用场景数组练习二维数组 数组的基本概念 什么是数组 数组可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。比如现实…

FOHEART H1数据手套:连接虚拟与现实,塑造智能交互新未来

在全新交互时代背景中&#xff0c;数据手套无疑是一种重要的科技产物。它不仅彻底改变了我们与虚拟世界的互动方式&#xff0c;更为我们提供了一种全新、更为直观的交互形式。 FOHEART H1数据手套结合了虚拟现实、手势识别等高新技术&#xff0c;用先进的传感技术和精准的数据…

Chatgpt AI newbing作画,文字生成图 BingImageCreator 二次开发,对接wxbot

开源项目 https://github.com/acheong08/BingImageCreator 获取cookie信息 cookieStore.get("_U").then(result > console.log(result.value)) pip3 install --upgrade BingImageCreator import os import BingImageCreatoros.environ["http_proxy"]…

一个概率论例题引发的思考

浙江大学版《概率论与梳理统计》一书中的&#xff0c;第13章第1节例2如下&#xff1a; 这个解释和模型比较简单易懂。接下来&#xff0c;第2节的例2是一个关于此模型的题目&#xff1a; 在我自己的理解中&#xff0c;此题的解法跟上一个题目一样&#xff0c;第二级传输后&…

Python-组合数据类型

今天要介绍的是Python的组合数据类型 整理不易&#xff0c;希望得到大家的支持&#xff0c;欢迎各位读者评论点赞收藏 感谢&#xff01; 目录 知识点知识导图1、组合数据类型的基本概念1.1 组合数据类型1.2 集合类型概述1.3 序列类型概述1.4 映射类型概述 2、列表类型2.1 列表的…

网络:从socket编程的角度说明UDP和TCP的关系,http和tcp的区别

尝试从编程的角度解释各种网络协议。 UDP和TCP的关系 从Python的socket编程角度出发&#xff0c;UDP&#xff08;User Datagram Protocol&#xff09;和TCP&#xff08;Transmission Control Protocol&#xff09;是两种不同的传输协议。 TCP是一种面向连接的协议&#xff0c…

OPENCV C++(八)HOG的实现

hog适合做行人的识别和车辆识别 对一定区域的形状描述方法 可以表示较大的形状 把图像分成一个一个小的区域的直方图 用cell做单位做直方图 计算各个像素的梯度强度和方向 用3*3的像素组成一个cell 3*3的cell组成一个block来归一化 提高亮度不变性 常用SVM分类器一起使用…

【Unity细节】Unity打包后UI面板消失是怎么回事

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐关于物体的动画碰到其他碰撞器后停止播放的问题⭐ 文章目录 ⭐关于物体的动画碰…

纯C#使用Visionpro工具1

各个工具的程序集名称 一般分类 一般情况是去掉Tool和Cog就是命名空间&#xff0c;如CogBlobTool对应于Cognex.Visionpro.Blob 也有特殊情况 忘了怎么办 可以借用ToolBlock引入工具后打开高级脚本查看 了解工具类和对象

微信小程序上传图片和文件

1.从微信里选择图片或文件上传 使用的vant的上传组件 原生用 wx.chooseMessageFile() html <!-- 从微信上面选择文件 --><van-uploader file-list"{{ file }}" bind:after-read"afterRead" max-count"{{3}}" deletable"{{ true…

CSS前端开发指南:创造精美的用户界面

简介&#xff1a; 《CSS前端开发指南&#xff1a;创造精美的用户界面》是一本旨在帮助读者掌握CSS技术&#xff0c;实现令人惊叹的前端用户界面的实用指南。无论您是初学者还是有经验的开发者&#xff0c;本书都将为您提供全面的知识和实用技巧&#xff0c;帮助您创建引人注目…

【ES】笔记-箭头函数的实践于应用场景

箭头函数的实践于应用场景 需求-1 点击 div 2s后颜色变成[粉色]从数组中返回偶数的元素 需求-1 点击 div 2s后颜色变成[粉色] html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport…

029 - integer types 整数类型

MySQL支持SQL标准整数类型 INTEGER&#xff08;或INT&#xff09;和 SMALLINT。作为一个可扩展标准&#xff0c;MySQL也支持整数类型 TINYINT&#xff0c;MEDIUMINT和 BIGINT。下表显示了每种整数类型所需的存储空间和范围。 表11.1 MySQL支持的整数类型的必需存储和范围 类型…

QGraphicsView实现简易地图4『局部加载-地图漫游』

前文链接&#xff1a;QGraphicsView实现简易地图3『局部加载-地图缩放』 当鼠标拖动地图移动时&#xff0c;需要实时增补和删减瓦片地图&#xff0c;大致思路是计算地图从各方向移动时进出视口的瓦片坐标值&#xff0c;根据变化后的瓦片坐标值来增减地图瓦片&#xff0c;以下将…

【小曾同学赠书活动】开始啦—〖测试设计思想〗

文章目录 ❤️ 赠书 —《测试设计思想》&#x1f31f; 书籍介绍&#x1f31f; 作者简介图书链接❤️ 活动介绍 — 赠送 3 本 ❤️ 赠书 —《测试设计思想》 首先提问 你知道测试设计思想有哪几类吗&#xff1f;你想奠定扎实的测试理论基础吗&#xff1f;你想改变关于你当前测试…

用python来爬取某鱼的商品信息(1/2)

目录 前言 第一大难题——找到网站入口 曲线救国 模拟搜索 第二大难题——登录 提一嘴 登录cookie获取 第一种 第二种 第四大难题——无法使用导出的cookie 原因 解决办法 最后 出现小问题 总结 前言 本章讲理论&#xff0c;后面一节讲代码 拿来练练手的&#xff…

2023牛客暑期多校训练营8-I Make It Square

2023牛客暑期多校训练营8-I Make It Square https://ac.nowcoder.com/acm/contest/57362/I 文章目录 2023牛客暑期多校训练营8-I Make It Square题意解题思路代码实现 题意 解题思路 这里有两种情况&#xff0c;即 ∣ s ∣ > ∣ t ∣ |s|>|t| ∣s∣>∣t∣和 ∣ s ∣…

领航优配:暑期旅游市场热度持续攀升,相关公司业绩有望持续释放

到发稿&#xff0c;海看股份涨停&#xff0c;中广天择、探路者、众信旅行等涨幅居前。 8月8日&#xff0c;在线旅行板块震动上涨&#xff0c;到发稿&#xff0c;海看股份涨停&#xff0c;中广天择、探路者、众信旅行等涨幅居前。 今年以来&#xff0c;国内旅行商场逐渐恢复。文…

获取 Android 的 SHA1 值

1、调试版&#xff0c;可以直接在 Android studio 中的 gradle 中查看。也可以用下面方法进行 前提要先确定签名文件所在的路径&#xff1a;调试版默认使用的签名文件是debug.keystore&#xff0c;文件处于 C 盘用户目录下的.android文件夹下。打开命令行工具&#xff0c; 1、…