啥?线上css样式错乱了?我本地运行没问题啊!

前言

测试A:那啥!抠图仔,线上样式怎么点着点着就出问题了。

前端:啥?线上css样式错乱了?你是不是有缓存啊!清下缓存试试。

测试A(内心戏:这抠图仔一有问题就赖缓存):清缓存后,还有啊!你看看吧!

前端:见鬼了,我本地没复现啊。

问题背景

在某次迭代中,在做产品体验的时候发现从申请记录页面跳转我的订单,在切回来,发现申请记录页样式错乱了。本地调试发现没有该问题。

问题定位

  1. 发现该问题测试环境会出现,本地环境未复现
  2. 打开调试面板,定位到样式出现问题元素。发现antd的样式(.ant-card)覆盖了项目中写的样式(.recordCard___yE53v)。如图:

为什么会出现这种场景?为什么该问题测试环境会出现,本地环境未复现?

调试发现 .ant-card可以从多个chunk文件引入,切换到network面板发现,2966....chunk.css文件是在我们跳转到我的订单页面才引入的。也就是我的订单页面按需加载组件打包出来的样式文件。

到这其实就定位到问题所在了,相同组件在不同页面按需加载的时候css文件被重复打包了。

开发环境不会,是因为我们导入组件是直接导入的node_modules的es模块的文件,如图:

为什么会出现在不同页面按需加载的时候css文件被重复打包了呢?

css 复制代码
dynamicImport: {
    loading: '@/Loading',
},

umi开启dynamicImport时,会启动按route分包,实现页面级别的按需加载,这种分包模式明显在处理antd的样式模块复用上出现了一些问题。

所以推荐项目开启该模式时,antd应该使用下面的方案二/三进行处理antd的样式,防止出现偶现的线上问题。

之前代码中会出现很多莫名其妙的!important去提高样式的权重,当然也有在页面级引入antd.css的,可能也是因为遇到了antd样式覆盖的问题。

输出方案

方案一:提高recordCard___yE53v的权重,不推荐。
  • 优点:

    • 改动对其他业务无任何影响。
  • 缺点:

    • 治标不治本,其他类似场景问题需要重复处理,代码入侵严重,心智成本比较高。
方案二:修改umi打包配置,对引入多次的antd组件样式不重复打包,需要根据实际项目情况选择。
  • 缺点:

    • 因为是在整个工程方面改动,影响面比较大,需要放在测试环境验证一段时间
    • 打包出来的verdors(3.8M),antdesigns(1.5M)js文件体积会比较大(实际压缩后不会这么大),一定程度上影响首屏加载速度。
  • 优点:

    • 采用分包,优化大文件资源,减少重复不必要代码。
less 复制代码
   // ...
   optimization: {
        splitChunks: {
          chunks: 'all',
          minSize: 30000,
          minChunks: 2,
          automaticNameDelimiter: '.',
          cacheGroups: {
               antd: {
                   name: 'antdesigns',
                   test: /[\/]node_modules[\/](antd|antd-mobile|@ant-design)[\/]/,
                   priority: 20,
               },
               vendors: {
                   name: 'vendors',
                   test({ resource }: any) {
                       return /[\/]node_modules[\/]/.test(resource);
                   },
                   priority: 10,
                },
          },
        },
      },
      // ...

优化后如图所示,申请记录页面跳转到我的订单页面再跳回来,.ant-card并没有多产生一个css文件引入。整个dist文件包体积从116.5M到108.4M,降低了8.1M。

方案三:在global.ts中引入antd.css文件。
arduino 复制代码
import 'antd/dist/antd.css'

可以作为方案二的一个互斥方案。

方案四:直接引入antd的less文件,不推荐
  1. 样式文件体积过大: 直接引入antd的less文件会导致整个antd样式库被打包到项目中,包括未使用的样式,从而增加了打包后的样式文件体积,影响页面加载性能。
  2. 影响页面渲染性能: 大量的样式文件会增加浏览器解析和渲染样式的时间,影响页面的加载速度和性能。
  3. 不利于 缓存 和更新: 直接引入antd的less文件会使样式文件无法通过浏览器缓存和CDN缓存等机制进行有效管理和更新。

webpack优化配置之splitChunks

webpack.docschina.org/plugins/spl...

默认值

  • 新的 chunk 可以被共享,或者模块来自于 node_modules 文件夹
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积)
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30
警告

选择了默认配置为了符合 Web 性能最佳实践,但是项目的最佳策略可能有所不同。如果要更改配置,则应评估所做更改的影响,以确保有真正的收益,所以我们做上述分包策略时,需要根据实际项目情况来处理。

less 复制代码
// 下面这个配置对象代表 SplitChunksPlugin 的默认行为。
module.exports = {//...
  optimization: {
    splitChunks: {
      // 有效值为 all,async 和 initial
      chunks: 'async',
      // 生成 chunk 的最小体积(以 bytes 为单位)。
      minSize: 20000,
      // 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块。
      minRemainingSize: 0,
      // 拆分前必须共享模块的最小 chunks 数。
      minChunks: 1,
      // 按需加载时的最大并行请求数。
      maxAsyncRequests: 30,
      // 入口点的最大并行请求数。
      maxInitialRequests: 30,
      // 强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
      enforceSizeThreshold: 50000,
      /**
      * 缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。
      * 但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。
      * 将它们设置为 false以禁用任何默认缓存组。
      */
      cacheGroups: {
        defaultVendors: {
          /** 
          * 控制此缓存组选择的模块。省略它会选择所有模块。
          * 注:使用/是因为要同时适配unix和windows系统
          */
          test: /[\/]node_modules[\/]/,
          // 优先级,默认值0
          priority: -10,
          // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块。这可能会影响 chunk 的结果文件名。
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
         },
       },
     },
   },
 };

webpack知识延展

线上和本地运行结果不一致,一直是一件让前端开发者头痛的问题。造成这种情况的原因之一呢?是因为场景不一样,webpack提供了两种模式。

  1. Development 模式:

    1. 在开发模式下,Webpack 会生成映射文件(source map),以便于调试代码。
    2. 生成的代码不会被压缩,可读性更强,包括注释和格式化。
    3. 启用了热模块替换(Hot Module Replacement),可以在不刷新页面的情况下更新模块。
    4. 通常会有更多的详细的错误日志和警告信息,方便开发者排查问题。
  2. Production 模式:

    1. 在生产模式下,Webpack 会对代码进行压缩和优化,以减小文件大小和提高性能。
    2. 不会生成映射文件,以减少额外的文件大小。
    3. 移除了开发时的一些辅助功能,如热模块替换,以提高性能。
    4. 通常会有更少的详细错误日志和警告信息,以减少额外的开销。

我们要杜绝发生线上和本地运行结果不一致的这种情况,需要我们深入了解项目中会用到的webpack,vite,rollup等前端工程化工具的内部打包机制。

知识补充

  • class="name1 name2"样式覆盖不是根据这里的类名先后来的 而是根据生成的样式表中的顺序。
相关推荐
极小狐5 分钟前
极狐GitLab 容器镜像仓库功能介绍
java·前端·数据库·npm·gitlab
程序猿阿伟17 分钟前
《Flutter社交应用暗黑奥秘:模式适配与色彩的艺术》
前端·flutter
rafael(一只小鱼)21 分钟前
黑马点评实战笔记
前端·firefox
weifont21 分钟前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情26 分钟前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
影子信息40 分钟前
css 点击后改变样式
前端·css
几何心凉1 小时前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js
小堃学编程1 小时前
前端学习(1)—— 使用HTML编写一个简单的个人简历展示页面
前端·javascript·html
hnlucky2 小时前
通俗易懂版知识点:Keepalived + LVS + Web + NFS 高可用集群到底是干什么的?
linux·前端·学习·github·web·可用性测试·lvs
懒羊羊我小弟2 小时前
使用 ECharts GL 实现交互式 3D 饼图:技术解析与实践
前端·vue.js·3d·前端框架·echarts