记录antdv3、v4混用和升级的尝试

1,873 阅读6分钟

背景

在一次需求开发中,有个功能的实现是需要用到支持虚拟滚动的树形UI组件,看了下项目之前用的是antd3.x的版本,而antd4.x中的tree组件才支持虚拟滚动。

依赖1.png

因此,我想到了两个解决方案:

  1. 项目中同时引入antd3.x和antd4.x
  2. 将旧有的antd3.x升级到4.x

一路下来踩了不少坑,这里做个记录分享给大家。


方案一、antd3.x & 4.x混用

① 别名安装

因为直接安装antd4.x会覆盖掉之前的版本,所以需要通过别名安装antd4.x

yarn add antd4@npm:antd@4

安装成功后可以看到package.json中多了antd4的库,node_modlues下也能看到名为antd4的目录

image.png

接下来便可以在组件中引入antd4的组件了,为了方便区分,在引入时也可以对组件进行重命名,因为原项目用的是umi中的antd3,样式文件会自动引入,所以这里我们要自己手动引入antd4的样式。

import { Button } from 'antd';
import { Button as Button4 } from 'antd4';
import "antd4/dist/antd.css";

const MixButton = () => {
  return (
    <>
      <Button>antd3_button</Button>
      <Button4 type="text>antd4_button</Button4>
    </>
  )
}

export default MixButton;

我们看到两个button都能展示出来,但是antd4的新特性textButton样式却没有生效

image.png

text_button.gif (官方的textButton是没有底色,鼠标悬浮上去才会有底色)

打开控制台一看发现,虽然有ant-btn-text这个样式类,但是样式被覆盖掉了。 原来是antd3的样式和antd4的样式起了冲突。

image.png

② ConfigProvider配置

如何隔离开antd3和antd4的样式,这个问题困扰了我很久,总不可能一个个手动加上吧。。

在查阅各种资料后,发现官网中ConfigProvider 可以通过设置prefixCls来改变antd样式的统一前缀。

image.png

再回到我们的代码

import { Button } from 'antd';
import { 
    Button as Button4,
    ConfigProvider as Antd4ConfigProvider
} from 'antd4';
import "antd4/dist/antd.css";

const MixButton = () => {
  return (
    <Antd4ConfigProvider prefixCls="antd4">
      <Button>antd3_button</Button>
      <Button4 type="text">antd4_button</Button4>
    </Antd4ConfigProvider>
  )
}

export default MixButton;

我们能看到样式的前缀都已经变成了antd4

image.png

当然我们引入的antd样式里的前缀还是还是ant,所以接下来还需要修改样式中的前缀。关于这里我也踩了好多坑尝试了几种方案

③ 样式隔离(less变量覆盖)

我们可以建立一个单独的 less 变量文件,引入这个文件覆盖 antd.less 里的变量。

@import '~antd4/es/style/themes/default.less';
@import '~antd4/dist/antd.less'; // 注意这里目录名是我们别名安装后的目录,要跟node_modules中的一致

@ant-prefix: antd4;

然后我们在全局引入(umi中是global.less),如果想要在组件内引入,记得设置disableCSSModules: true

// global.less
@import "./my-antd.less";

运行代码可以看到样式成功隔离了

image.png

③ 样式隔离(编译css)

我还尝试了另一种方案,有点复杂这里只做记录,并不推荐

node_modules下 antd4/dist/.css实际上是通过less文件编译生成的,看过源码的同学应该可以注意到在variable.less下就有@ant-prefix这个变量。

image.png

我们可以通过lessc命令修改@ant-prefix并编译生成新的css文件

lessc --js --modify-var='ant-prefix=antd4' node_modules/antd4/dist/antd.varbliless antd4.css
                                           // 这里对应antd4在node_modules中的位置  输出的文件名

没有全局安装less也可以在package.json中添加命令利用npm命令来运行

// package.json
"scripts": {
    "myLessc": "lessc --js --modify-var='ant-prefix=custom' node_modules/antd4/dist/antd.less custom.css"
}

// shell
npm run myLessc

运行完命令后可以在根目录下看到编译后的文件antd4.css 里面前缀都变成了antd4

image.png

在引入新的样式文件后,antd4按钮的样式成功与antd3隔离了

import { Button } from 'antd';
import { 
    Button as Button4,
    ConfigProvider as Antd4ConfigProvider
} from 'antd4';
// import "antd4/dist/antd.css";
import "@/antd4.css";

const MixButton = () => {
  return (
    <Antd4ConfigProvider prefixCls="antd4">
      <Button>antd3_button</Button>
      <Button4 type="text">antd4_button</Button4>
    </Antd4ConfigProvider>
  )
}

export default MixButton;

当然umi项目默认开启了css modules模式,是不支持直接引入css文件的,需要在config中设置disableCSSModules: true才能生效

image.png

在经历这么一番折腾后,终于实现了样式隔离。。

image.png

③ 样式隔离(配置less-loader)

前面的方法都是全量引入样式,当然有人会觉得这么做既麻烦又不优雅,为什么不直接用less-loader按需加载呢?

还记得上面说到别名安装antd4后,样式文件是通过手动引入的吗

import "antd4/dist/antd.css";

其实在这一步就可以通过babel-plugin-import来实现按需加载组件样式

对于umi项目来说需要在config.js文件中添加配置


export default {
    ...
    extraBabelPlugins: [
    [
        "import",
        { 
            libraryName: "antd4", // 指定要按需加载的库的名称
            libraryDirectory: "es", // 告诉插件从哪个子目录中按需加载模块。
            style: true // 指定是否加载样式文件,以及样式文件的加载方式。
            // true:加载对应模块的 CSS 文件。
            // "css":加载对应模块的 CSS 文件。
            // "less":加载对应模块的 Less 文件
        }
    ]
  ],
}

然后需要通过less-loader的modifyVars来对antd4中的@ant-prefix进行修改

// config.js
export default {
    ...
    lessLoaderOptions: {
        javascriptEnabled: true,
        modifyVars: {
          '@ant-prefix': 'antd4',
        },
      },
}

umi项目也可以直接设置theme配置项

// config.js

theme: {
   '@ant-prefix': 'antd4'
},

但这样问题就来了,因为lessLoader会对所有less文件都做处理,因此antd3的变量也会被修改到,所以antd3 button的样式又失效了。。。

image.png

那么可以在配置less-loader时用include限制处理的范围,umi中配置webpack使用的是chainWebpack,写法如下


chainWebpack: config => {
    config.module
      .rule('my-less')
      .test(/\.less$/)
      .include.add(path.resolve(__dirname, '../node_modules/antd4'))
      .end()
      .use('style-loader')
      .loader('style-loader')
      .end()
      .use('css-loader')
      .loader('css-loader')
      .end()
      .use('less-loader')
      .loader('less-loader')
      .options({
        modifyVars: {
          // 'primary-color': '#1DA57A',
          'ant-prefix': 'antd4',
        },
        javascriptEnabled: true,
      });
}

重新运行项目,不出意外的出意外了,控制台报错

image.png

升级了less-loader的版本还是不行。。不知道是不是跟umi的af-webpack有关。

我拿非umi项目试了一下,这个办法是行的通的,只能说umi不想让我好过QAQ

目前就在这卡住了。。后面有时间再来研究研究

再次尝试(2024.8.24)

试了一下umi项目只要去掉style-loader和css-loader就可以成功运行了!(webpack项目还是正常的配置webpack就行啦)

chainWebpack: config => {
    config.module
      .rule('my-less')
      .test(/\.less$/)
      .include.add(path.resolve(__dirname, '../node_modules/antd4'))
      .end()
      // .use('style-loader')
      // .loader('style-loader')
      // .end()
      // .use('css-loader')
      // .loader('css-loader')
      // .end()
      .use('less-loader')
      .loader('less-loader')
      .options({
        modifyVars: {
          // 'primary-color': '#1DA57A',
          'ant-prefix': 'antd4',
        },
        javascriptEnabled: true,
      });
}

跑起来可以看到antd4的textButton以及primary-color的改动都生效了

button_color.gif

总算是解决了这个问题,欣慰~

方案二、antd3.x 升级到 antd4.x

其实在混用踩了这么多坑之后,我其中尝试过升级方案,正好官网也提供了升级工具,详细可以参考官网教程 4x.ant.design/docs/react/…

安装好升级工具后直接运行即可

yarn global add @ant-design/codemod-v4

antd4-codemod src

对于antd4破坏升级的组件(如From),会自动引入@ant-design/compatible兼容包保持运行。

- import { Form, Input, Button, Mention } from 'antd'; 
+ import { Form, Mention } from '@ant-design/compatible'; 
+ import '@ant-design/compatible/assets/index.css'; 
+ import { Input, Button } from 'antd';

运行升级工具后,对于无法自动修改的部分会在控制台提示手动修改,修改完后可以再次运行命令来检查。

可以看到一堆文件都被升级工具修改了 image.png

运行完成后控制台会提示要添加对应的依赖。

image.png

在package.json中添加好再次安装即可

"dependencies": {
    "antd": "^4.0.0",
    "@ant-design/compatible": "^1.1.2",
    "@ant-design/icons": "^4.0.0",
}

最后成功运行代码~

不过antd3和4有些写法并不兼容,比如说antd3中Row组件下的元素会尽可能占满一行,而antd4中只是占内部元素宽度。所以升级后还是需要去详细测试一遍的。