chenxiaochun / blog

🖋️ChenXiaoChun's blog
179 stars 15 forks source link

在一个项目中同时使用 antd3 和 antd4 #73

Open chenxiaochun opened 2 years ago

chenxiaochun commented 2 years ago

Antd4 这个版本已经发布有很长时间了。但是,你应该还有些老项目依然在使用 antd3。虽然你垂涎于 antd4 的新特性,但是由于这些原因你又没办法直接升级:

  1. 老项目依然在更新迭代,直接升级 antd4,会导致大量代码需要重构。尤其是涉及到 Form 表单相关的代码
  2. 使用 antd 官方提供的升级工具。但它会生成很多冗余的胶水代码,这个对于一个代码洁癖者来说,是很难忍受的一件事情

由于这几个原因,我们走了另外一条途径,就是让项目中同时存在 antd3 和 antd4 两个版本。也就是说,将 antd 的两个版本都同时引入到项目中。不改动之前 antd3 的老代码,新的页面使用 antd4 来开发。然后在空余时间再慢慢改造老的代码,直到将 antd3 的相关代码慢慢改造干净为止

但是,将两个版本都引入进来,会存在下面几个问题:

  1. Antd4 和 antd3 的 js 代码暴露的全局变量都是antd。所以,两个 js 文件如果都直接引入进来,后面版本的全局变量就会覆盖之前的

一种解决办法就是在加载完 antd4 之后,紧接着添加一行window.antd4 = antd。这样 antd4 暴露的全局变量就被缓存起来了。另外一种方式就是将官方的 antd4.js 进行重新编译,将其全局变量直接暴露为antd4,后面详说此种方式

<script src="http://unpkg.com/antd@4.16.6/dist/antd-with-locales.min.js"></script>
<script>window.antd4 = antd</script>
<script src="http://unpkg.com/antd@3.26.19/dist/antd-with-locales.min.js"></script>
  1. 页面中引入两个版本的 css 文件,样式也存在冲突,需要将 antd4.css 进行重新构建,以修改其前缀,这个后面也会详说:

    <link rel="stylesheet" href="http://unpkg.com/antd@3.26.19/dist/antd.min.css" />
    <link rel="stylesheet" href="http://unpkg.com/antd@4.17.4/dist/antd.min.css" />
  2. 修改 vite 或者 webpack 的externals的依赖映射

export default defineConfig({
  plugins: [
    react(),
    viteExternalsPlugin({
      react: 'React',
      'react-dom': 'ReactDOM',
      antd: 'antd',
      antd4: 'antd4'
    }),
  ],
})

如此在页面中使用的时候:

import { Button } from 'antd' // 引用 antd3 的组件
import { Button } from 'antd4' // 引用 antd4 的组件
  1. 在你项目的入口文件中,使用ConfigProvider修改页面中引用的组件前缀
    
    import { ConfigProvider } from 'antd'
    import { ConfigProvider as ConfigProvider4 } from 'antd4'
...

5. `package.json`中的依赖项。使用这种方式安装 antd4,可以让两个版本同时存在

```json
{
  "dependencies": {
    "antd": "3.26.19",
    "antd4": "npm:antd@4.16.6",
  }
}

即使上面配置了externals,也在本地进行安装。是为了可以在 ts 项目中引用其类型定义

从工程上解决上述问题

上面只是一个大概的步骤,下面详细讲一下如何使用 vite 的 lib 模式对 antd4.0 进行重新构建

这里使用 vite 创建一个新工程,并安装相关依赖:

yarn create vite antd4-build --template react-ts
yarn add vite-plugin-externals -D
yarn
  1. 在 src 目录下创建一个入口文件,引入 antd4.0 的相关文件。例如就叫antd.ts
import 'antd/dist/antd.less' // 引入其 less 文件

// 引入中英两种语言包。antd.js 中包含了太多的语言包,其实我们大多数都用不到。这里都将其排除出去
// 其实英文语言包,可能很多系统也用不到,这里为了保险,就将包含进来了
import zh_CN from 'antd/lib/locale/zh_CN'
import en_US from 'antd/lib/locale/en_US'

const locales = {
  zh_CN,
  en_US
}

// 将 antd.js 直接引入进来,并导出去
export * from 'antd'

// 导出语言包
export { locales }
export default defineConfig({
  build: {
    lib: {
      entry: './src/antd.ts',
      name: 'antd4',
      fileName: 'antd4',
    },
    outDir: 'dist',
    rollupOptions: {
      output: {
        assetFileNames: (assetInfo) => {
          if (assetInfo.name == 'style.css'){
              return 'antd4.css'
          }
          return assetInfo.name
        }
      }
    }
  },
})
  1. 修改 antd.css 的前缀:
export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        modifyVars: {
          'ant-prefix': 'ant4', // 这里最重要
          'form-item-margin-bottom': '14px',
          // 'iconfont-css-prefix': 'ant4icon'
        }
      }
    }
  }
})
  1. 添加时间戳和版本号,以方便查看:
export default defineConfig({
  plugins: [
    Banner(`${new Date().toLocaleString('en-US', {hour12: false})} antd@${packageJson.dependencies.antd}`)
  ],
})
  1. 对 antd 的三个依赖库进行外置,否则会和引入的项目产生冲突
export default defineConfig({
  plugins: [
    react(),
    viteExternalsPlugin({
      react: 'React',
      'react-dom': 'ReactDOM',
      moment: 'moment'
    }),
  ],
})
  1. 最后执行yarn vite build,会生成三个文件:
antd4.es.js
antd4.umd.js
antd4.css

将其放到自己的项目中,或者公司的 cdn 中直接引用就行了

完整 vite 配置:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import Banner from 'vite-plugin-banner'
import packageJson from './package.json'
import { viteExternalsPlugin } from 'vite-plugin-externals'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    viteExternalsPlugin({
      react: 'React',
      'react-dom': 'ReactDOM',
      moment: 'moment'
    }),
    Banner(`${new Date().toLocaleString('en-US', {hour12: false})} antd@${packageJson.dependencies.antd}`)
  ],
  server: {
    port: 5000,
  },
  build: {
    lib: {
      entry: './src/antd.ts',
      name: 'antd4',
      fileName: 'antd4',
    },
    outDir: 'dist',
    rollupOptions: {
      output: {
        assetFileNames: (assetInfo) => {
          if (assetInfo.name == 'style.css'){
              return 'antd4.css'
          }
          return assetInfo.name
        }
      }
    }
  },
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        modifyVars: {
          'ant-prefix': 'ant4',
          'form-item-margin-bottom': '14px',
          // 'iconfont-css-prefix': 'ant4icon'
        }
      }
    }
  }
})
loveky commented 2 years ago

👏👏👏