在项目中渐进式的升级组件库

前言

众所周知,在前端项目不断的更新迭代的过程中,总会遇到所依赖的库升级的问题,对于一些已经有了一定规模的项目来说,对所依赖的库进行升级,往往是一个令人头痛的事情,特别是一些库大版本升级时,进行了很多破坏性更新的情况下。随着项目的越来越大,升级的成本也就越来越高。

为什么要升级

对项目中依赖的库进行升级,主要是因为旧的版本官方基本都不会再新增功能,只是修复一些bug,当你遇到问题时,也很难获得官方的支持,特别是对于公司内部的库来说,一方面公司层面可能会要求各个项目进行升级,另一方面则是当你需要公司库的某个功能,但它又只在新版本提供了后,你就会面临究竟是自己写一套,还是升级版本的局面,而基础库的部门,大概率不会为了你的功能,而在旧版本新增功能。

如何升级

最近在为自己负责的项目进行组件库的升级,组件库时公司内部的组件库,项目依赖的版本时2.x版本,而最新版本已经到了4.x。这里就只分享一下升级方案

基本思路

在项目中共存两个版本的库,然后根据路由,一个路由一个路由的进行升级,但这里涉及到两个问题,一个是如何让一个项目存在两个版本的库,另一个则是样式问题,众所周知,css样式是会互相覆盖的,这就导致即使两个版本,如果这两个版本采用的css类名基本一致,就会样式冲突,很不幸,我的项目就是这样的。

如何让两个版本共存

我这里采用的方式是使用别名的方式,即这样子:

  • npm:
perl 复制代码
npm install package-compatible@npm:package@^2.2.7
  • yarn:
perl 复制代码
yarn add package-compatible@yarn:package@^2.2.7
  • pnpm:
perl 复制代码
pnpm add package-compatible@npm:package@^2.2.7

这样子在项目里就可以这样引用了

javascript 复制代码
import { Table } from "package-compatible"

当然,你也可以fork一份,发一个新的包到npm上,但是我这里一没有库的源码,二是公司内申请组件库还挺麻烦的,就没有这么干

样式隔离

  • 如果你所有使用的组件库支持设置prefixCls的话,直接对新版本设置prefixCls即可,后面的不用看了,这里解释下为什么不对旧版本设置prefixCls,因为项目里很多修改组件库的样式地方,如果你改了旧版本的prefixCls,这些地方大概率都不会生效了,如果不支持设置prefixCls,那么可以继续看下去
  • 现在版本已经共存了,那样式怎么做隔离呢,这里我采取的方案是为项目里所有的css规则增加一个前置css类,这个前置css类被设置在body上,根据路由来动态切换body上的css类

如何添加前置css类呢

我这里采取的方案是使用PostCSS 的插件 postcss-prefix-selector 来实现,思路是先给所有选择器加上前缀 .ui-isolate,然后根据 CSS 的文件路径将其替换成 .ui-4 或 .ui-3

  • 关键点
    • 对于body xxx的规则,不能直接加前缀,而是要改成body.ui-isolate,因为我们采用的方案是在body上切换css类,如果你不处理,就会变成 .ui-isolate body,那这条css规则就没有办法被应用了
    • 对于html xxx,或者:root的规则,不能增加前缀,因为前缀class是设置在body上的,无法应用在html的规则上,:root同理
    • 对于项目中使用了css-module的地方,要进行特殊处理,原因是因为我们采用的是postCSS的插件,postCSS的插件是在css-loader之前处理的,会导致css-module对你的前缀也进行hash,导致规则失效,举例来说就是原本的规则是 .container,我们处理后变成了 .ui-isolate .container ,然后被css-loader处理后就变成了 .ui-isolate-xxxxx .container-xxxxxx,这里我们要进行这样的一个处理,将它处理成 .container.container,这样权重就被提升了,而规则也不会失效

配置的代码如下:

javascript 复制代码
module.exports = {
  loader: 'postcss-loader',
  options: {
    postcssOptions: {
      plugins: [
        ['postcss-prefix-selector', {
          // use this string to identify prefixed selectors, which will be
          // replaced to .ui-4/3 according to the file path.
          prefix: '.ui-isolate',
          transform: (prefix, selector, prefixedSelector, filePath, rule) => {
            // html/:root
            if(selector.startWith("html")||selector.startWith(":root")){
              return selector;
            }

            // 自定义的css,涉及css-module的情况
            if (filePath.includes('module') && !filePath.includes('node_modules')) {
              const [firstCls,...restCls] = selector.split(" ");
              return [`${firstCls}${firstCls}`,...restCls].join(" ");
            }

            // body的样式处理
            if(selector.startWith("body"){
              prefixedSelector = selector.replace('body', 'body.ui-isolate')
            }
            
            // v4 prefix
            if (filePath.includes('基础组件库@4') || filePath.includes('业务组件库@4')) {
              // a possible result might be ".ui-4 .ui-button"
              return prefixedSelector.replace('.ui-isolate', '.ui-4');
            }
            // v3 prefix
            if (filePath.includes('基础组件库@3') || filePath.includes('业务组件库@3')) {
              // a possible result might be ".ui-3 .ui-button"
              return prefixedSelector.replace('.ui-isolate', '.ui-3');
            }
            // default not prefix
            return prefixedSelector;
          },
        }],
      ],
    },
  },
}

路由切换代码:

javascript 复制代码
document.body.classList.add("ui-isolate","ui-3")
// 如果是已经完成改造的路由
if(isV4Complete){
	document.body.classList.replace("ui-3","ui-3")
}

为什么要把前缀放到body上

因为最高只能放到body上,如果你知道如何放到html,最高可以覆盖所有场景,因为有些组件会直接将直接渲染到body上,而不是项目的根root元素上

为什么要为除了组件库外的其他css规则也增加前缀

因为组件库的css规则的权重都提升了,如果其他的css规则不提升,就会出现一些原有的样式失效问题,而为所有的css规则都增加一样的权重,就等于没增加权重

最后

在完成上述改造后,大家就可以在项目中新的路由里愉快的使用新版本的库了,同时可以对旧有的库慢慢进行升级,可以为自己设置一些任务,每个月升级多少路由,逐步进行全部替换,最后替换完成,就可以删除这些兼容代码了

参考文档

多版本共存------巨型项目组件库升级的必经之路

相关推荐
weixin_382395231 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__1 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.2 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~3 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华4 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子6 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅6 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni7 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学8 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端