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

news2025/2/11 21:04:28

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

前言

最近在做公司几个项目性能优化,整理出一些比较有用且常见的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') {
    // 集团&饿了么 DAILY
    env = 'dev';
  } else if (/^(pre|ppe)-\w+./.test(domain)) {
    // 集团&饿了么 PRE
    env = '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.coloradmin.cn/o/864399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

还不会python 实现常用的数据编码和对称加密?看这篇文章就够啦~

♥ 前 言 相信很多使用 python 的小伙伴在工作中都遇到过&#xff0c;对数据进行相关编码或加密的需求&#xff0c;今天这篇文章主要给大家介绍对于一些常用的数据编码和数据加密的方式&#xff0c;如何使用 python 去实现。话不多说&#xff0c;接下来直接进入主题&#xf…

install imap error

【错误翻译】 Try to run this command from the system terminal. Make sure that you use the correct version of pip installed for your Python interpreter located at D:\Program Files (x86)\Python\Python39\python.exe. 尝试从系统终端运行此命令。请确保使用安装在…

【UE4 RTS】09-Day and Night

前言 本篇博客实现的效果是太阳和天空会随着游戏时间的变化而变化。 效果 步骤 1. 设置“LightSource”为可移动的 2. 新建一个文件夹&#xff0c;命名为“Lighting” 3. 打开游戏状态“RTS_GameState_BP”&#xff0c;添加一个函数命名为“GetGameSpeed” 添加一个浮点类型…

利用 Splashtop Enterprise 改善公司的网络安全

在我们日益数字化的世界中&#xff0c;对强有力的网络安全措施的需求从未像现在这样迫切。随着组织扩大其数字足迹并采用远程办公解决方案&#xff0c;他们面临着一系列不断变化的挑战。 威胁行为者不断寻找利用漏洞的新方法&#xff0c;这使得企业保持领先地位至关重要。俗话…

【Cartopy】库的安装和瓦片加载(天地图、高德等)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 Cartopy基础入门 【Cartopy】库的安装和天地图瓦片加载 【Cartopy】【Cartopy】如何更好的确定边界显示 【Cartopy】【Cartopy】如何丝滑的加载Geojso…

搜狗输入法曝加密系统漏洞(已修复),黑客可窃取用户输入的内容

近期&#xff0c;来自加拿大多伦多大学公民实验室的研究人员在国内热门输入法——搜狗输入法的加密系统中发现了漏洞&#xff0c;能允许网络监听者破译用户的输入内容。目前该漏洞已得到修复。 研究人员发现漏洞的软件版本涉及三大主流系统&#xff0c;分别是Windows 13.4版本、…

教程分享:如何制作一个旅游路线二维码?

吃一成不变的早餐&#xff0c;九点出门还会遇见楼下遛狗的大爷&#xff0c;老板掐着表发起了会议邀请&#xff0c;窗外还是那几棵树&#xff0c;天空依旧灰蒙蒙的&#xff0c;羊了个羊第二关还是过不去&#xff0c;理发店的小哥又倚在门口抽烟…… 大多时候&#xff0c;我们的…

Small Tip: 如何Debug Start Routine

我也不知道咋地&#xff0c;在generated ABAP里面打断点进不去。 我也不晓得怎么弄&#xff0c;今天反正是硬找着去弄。不晓得有没有其他好办法。有知道的小伙伴评论下吧。 1、 在DTP里面选Before Transformation&#xff0c;要去debug start routine选这个就够了。其他的随意…

轻装上阵,不调用jar包,用C#写SM4加密算法【卸载IKVM 】

前言 记得之前写了一个文章&#xff0c;是关于java和c#加密不一致导致需要使用ikvm的方式来进行数据加密&#xff0c;主要是ikvm把打包后的jar包打成dll包&#xff0c;然后Nuget引入ikvm&#xff0c;从而实现算法的统一&#xff0c;这几天闲来无事&#xff0c;网上找了一下加密…

关于三维模型OSGB格式轻量化压缩出现分层现象分析

关于三维模型OSGB格式轻量化压缩出现分层现象分析 三维模型OSGB格式的轻量化压缩分层现象是指在进行几何压缩和纹理压缩过程中&#xff0c;由于不同部位的信息复杂度存在差异&#xff0c;可能会出现某些部位被处理过度&#xff0c;导致其失去了细节和质感的现象。本文将从几何…

“解锁IDEA的潜力:高级Java Maven项目配置指南”

目录 前言&#xff1a;流程目录&#xff1a;1.确保Java和Maven已安装检查Java是否已正确安装并配置环境变量 2.创建一个新的Maven项目导航到要创建项目的目录配置Maven运行以下命令创建一个新的Maven项目 3.配置项目的pom.xml文件打开项目根目录下的pom.xml文件配置Web.xml 4.配…

什么是绩效管理?绩效管理包括哪些内容?

阅读本文您可以了解&#xff1a;1、绩效管理的定义&#xff1b;2、绩效管理的内容&#xff1b; 一、什么是绩效管理 绩效管理是一种组织和管理方法&#xff0c;旨在确保员工的工作与组织的目标保持一致&#xff0c;以及激励和提高员工的工作表现。它涉及设定明确的目标和标准&…

【Spring】-Spring的IoC和DI

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;什么是spring&#xff1f;IoC容器是什么&#xff1f;如何使代码解耦合&#xff1f;IoC的核心原理&#xff0c;IoC的优点。依赖注入/对象装配/…

VBA_MF系列技术资料1-152

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于定…

沁恒ch32V208处理器开发(三)GPIO控制

目录 GPIO功能概述 CH32V2x 微控制器的GPIO 口可以配置成多种输入或输出模式&#xff0c;内置可关闭的上拉或下拉电阻&#xff0c;可以配置成推挽或开漏功能。GPIO 口还可以复用成其他功能。端口的每个引脚都可以配置成以下的多种模式之一&#xff1a; 1 浮空输入 2 上拉输入…

8年经验之谈 —— 35岁以上的测试开发工程师都去哪里了?

“测试开发工程师就是吃青春饭&#xff01;” “35岁就是测试开发工程师的天花板&#xff0c;没有工作机会了。” “测试开发工程师趁早转行&#xff0c;不然迟早失业。” …… 网上对测试开发工程师不友好的言论非常多。真的是这样吗&#xff1f;如果不是这样&#xff0c;…

Cesium 1.107+ 自定义类支持 readyPromise

由于cesium 1.107 的图元(Primitive) 已经不支持 readyPromise。 但是个人感觉比较好用,于是用了一个插件来实现。 用法: // 定义图元并添加,和之前一样 const boxGreen new BoxPrimitive({color: "#ff0000" }) viewer.scene.primitives.add(boxGreen.primitive)/…

lazada、shopee店铺如何利用测评提高权重和排名?

在 lazada、shopee平台上开店后&#xff0c;卖家们必须对店铺的权重进行更多的关注。如果店铺的权重越高&#xff0c;那么它就会带来更多的流量和更多的订单&#xff0c;那么在 lazada、shopee平台上开设一家店铺&#xff0c;该怎样增加它的店铺权重和排名呢&#xff1f; laza…

算能BM1684X部署手写数字识别模型

大致流程--------------------------------------------------------------- 参考《sophon-sail_zh》 移植步骤------------------------------------------------------------------------ 首先搭建好自己的网络模型&#xff0c;并导出为onnx格式--具体可以参照--> GitH…

aspose 使用ftl模板生成word和pdf

1 先找到word模板&#xff0c;用${}&#xff0c;替换变量&#xff0c;保存&#xff0c;然后另存为xml,最后把xml后缀改成ftl。 如下图&#xff1a; word 模板文件 ftl模板文件如下: 2 代码生成 下面函数将ftl填充数据&#xff0c;并生成word和pdf /*** * param dataMap 模板…