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

2023-10-31

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

前言

最近在做公司几个项目性能优化,整理出一些比较有用且常见的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,优化是灵活的,需要根据自己的场景来定,对你有帮助那就最好不过啦。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过这些case,我把项目LCP时间减少了1.5s 的相关文章

随机推荐

  • python捕获异常时,打印异常的类型、报错文件、与报错所在的行

    捕获异常 异常的完整代码是 try raise Exception wa except print 报错 else print 没有报错 finally print 程序关闭 得到结果 报错 程序关闭 一般程序里的 try与except是一
  • 如何优化一个肽预测模型

    要优化一个肽预测模型 首先需要考虑的是输入数据的质量 确保输入的数据是完整的 正确的 而不是噪声数据 此外 还需要考虑模型的训练方式 比如是否使用正则化和提前停止来确保不会过拟合训练数据 最后 应该尝试在模型中使用不同的参数来改善模型的性能
  • 02 Java基本数据结构之队列实现

    系列文章目录 01 Java基本数据结构之栈实现 02 Java基本数据结构之队列实现 03 Java基本数据结构之优先级队列 04 Java基本数据结构之链表 如有错误 还请指出 文章目录 系列文章目录 前言 一 队列 简述 二 栈 数组
  • 【Hyperledger Fabric】学习笔记1—— 区块链介绍

    目录 1 区块链介绍 1 1 区块链技术起源 1 1 1 区块链技术 1 1 2 区块链技术发展 1 2 区块链核心技术 1 2 1 定义 1 2 2 区块链技术原理 1 2 3 区块链工作过程 1 3 区块链开发平台 1 3 1 公有链平
  • GIT使用教程(十五步吃透,全网最详细)

    一 安装GIT 到官网下载GIT https git scm com downloads 二 创建仓库 在要创建仓库的文件夹空白地方点击右键 在弹出的菜单中点击 GIT Bash Here 然后初始化仓库 git init 成功后该文件夹中
  • MySQL配置和设置问题小结

    问题1 root Tony ts tian bin mysqladmin uroot password kaka123 mysqladmin connect to server at localhost failed ERROR 1045
  • [4G+5G专题-144]: 测试-频谱分析仪工作原理与测试结果分析

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 123222945 目录 前言 第1章
  • RSA/数字证书/签名原理详解

    文中首先解释了加密解密的一些基础知识和概念 然后通过一个加密通信过程的例子说明了加密算法的作用 以及数字证书的出现所起的作用 接着对数字证书做一个详细的解释 并讨论一下windows中数字证书的管理 最后演示使用makecert生成数字证书
  • 优惠卷测试案例

    提示 过期优惠卷 不同等级的用户 叠加使用 退款 支付失败 取消支付 退款中 订单信息 网络问题 退货 兼容性 优惠券是否可以正常使用 外观是否与UI保持一致 部分商品是否能正常使用 购买商品的时候会不会提示使用优惠券 优惠券是否能分享 分
  • git锁住如何解决GitLab: Your account has been blocked.

    今天用gitpush和pull的时候出现了一个问题 报了一个错误 GitLab Your account has been blocked 然后我怀疑是账号错误 然后发现账号密码对 后来发现是两个git账号同时占用了一个目录 强制删除目录下
  • 数据结构【堆】的认识及建立

    目录 一 堆 1 什么是堆 2 堆的存储方式 二 堆的建立与存储 三 堆的应用 1 堆排序 2 对顶堆 一 堆 1 什么是堆 堆 Heap 是一种特殊的完全二叉树结构 其中最大堆 Max Heap 或最小堆 Min Heap 的每个节点的键
  • Maven-Failed to parse POMs

    Maven Failed to parse POMs 错误描述信息 产生错误的原因 解决办法 依赖关系 错误描述信息 ERROR Failed to parse POMs hudson remoting ProxyException hud
  • mmdetection学习&训练测试自己的数据集

    一 本机使用环境 商汤科技和香港中文大学联合开源的深度学习目标检测工具箱mmdetection源码地址 Ubuntu16 04 Cuda9 0 cudnn7 5 Python3 6 GCC 7 2 Anaconda3 二 环境配置 官方配置
  • 无法连接 SQL Server 不可用或不存在 无法连接, SQL Server 不存在或拒绝网络访问..请问这是怎么回事?...

    远程连接sql server 2000服务器的解决方案 一 看ping 服务器IP能否ping通 这个实际上是看和远程sql server 2000服务器的物理连接是否存在 如果不行 请检查网络 查看配置 当然得确保远程sql server
  • CUDA 6.0在 VS 2010下的安装和配置

    CUDA 6 0在 VS 2010下的安装和配置 安装前准备 CUDA 6 0 安装包 下载地址 https developer nvidia com cuda downloads VS 2010 安装 这个直接下个免费的就行 Visual
  • 信息打点-公众号服务&Github监控&供应链&网盘泄漏&证书图标邮箱资产

    文章目录 微信公众号 获取 三方服务 Github监控 开发 配置 源码 网盘资源搜索 全局文件机密 敏感目录文件 目录扫描 爬虫 网络空间进阶 证书 图标 邮箱 实战案例四则 技术分享打击方位 微信公众号 获取 三方服务 1 获取微信公众
  • Linux C利用Socket套接字进行服务器与多个客户端进行通讯

    http blog csdn net returningprodigal article details 51916754 服务器端 html view plain copy print include
  • C++/Python机器学习—感知机(二分类)

    一 Python import numpy as np import matplotlib pyplot as plt 定义预测函数 def predict x w b 计算特征向量和权重向量的点积 dot product np dot x
  • chrome浏览器 docker_使用docker安装elasticsearch

    1 使用docker安装 拉取镜像docker pull elasticsearch 6 5 4 创建容器docker create name elasticsearch net host e discovery type single n
  • 通过这些case,我把项目LCP时间减少了1.5s

    您好 如果喜欢我的文章 可以关注我的公众号 量子前端 将不定期关注推送前端好文 前言 最近在做公司几个项目性能优化 整理出一些比较有用且常见的case来分享一下 A项目优化 白屏相关 DNS预连接 资源预解析 对于公共域名g alicdn