将一个老项目的 Web 应用框架升级为 Umi 4.0

背景

该项目使用的 Web 应用模板是react-starter-kit,使用的是 2017 年的版本,目前主要遇到如下几个问题:

  1. 一直没有太大的升级,只对依赖的 React、Webpack、AntDesign 进行必要的升级,升级和维护成本也比较高,并且缺少对 React Router、Vite、TypeScript 的支持,不利于之后的代码维护和新特性的使用。
  2. 打包性能比较差,目前需要 1 分 08 秒。

升级中遇到的问题

获取 URL 的查询字符串

在 react-starter-kit 中,可以通过 context 获取查询对象,例如:

js 复制代码
function action(context) {
  const { query } = context;
  return {
    component: <Admin query={query} />,
  };
}

而在 umi 中,在函数组件里可以通过 useSearchParams 获取,例如:

js 复制代码
function Admin() {
    const [searchParams, setSearchParams] = useSearchParams();
    return <div>{searchParams.get('name')}<div>
}

在类组件里可以通过 createSearchParams 获取,例如:

js 复制代码
class Admin extends React.Component {
  const searchParams = createSearchParams(window.location.search);
  render() {
    return <div>{searchParams.get('name')}<div>
  }
}

考虑到老项目里都是处理之后通过 props 传递给组件,则可以抽象成一个高阶组件,单独处理获取 URL 的查询字符串的逻辑,这样大部分组件都不需要进行改动,例如:

高阶函数 WrappedComponent

js 复制代码
const getSearchParams = () => {
  const searchParams = createSearchParams(window.location.search);
  if (!searchParams) return {}
  const params = {};
  for (const [key, value] of searchParams.entries()) {
    params[key] = value;
  }
  return params;
}

export default function WrappedComponent(WrappedComponent) {
  return (props) => {
    const searchParams = getSearchParams()
    return <WrappedComponent {...props} {...searchParams} />
  }
}
js 复制代码
class Admin extends React.Component {
  render() {
    return <div>{this.props.name}<div>
  }
}
export default WrappedComponent(Admin)

如何配置国际化

Ant Design 里支持使用 ConfigProvider 进行国际化配置,可以放在任何需要国际化的组件中,而我们希望该配置是全局性的,所以在 Umi 中可以配置在 layouts/index.tsx 里,例如:

js 复制代码
import { Outlet } from 'umi';
import {ConfigProvider} from 'antd'
import zhCN from "antd/locale/zh_CN";

export default function Layout() {
  return (
      <ConfigProvider locale={zhCN}>
          <div>
            <Outlet />
          </div>
      </ConfigProvider>
  );
}

在这里有一点需要注意,因为在老项目里我使用的日期库是 moment,则还需要加上 moment 的国际化配置,例如:

js 复制代码
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

如果使用的 Ant Design 5.0 默认的 Day.js,则也需要进行相应的配置。

静态部署问题

我们老项目是使用静态站点托管,需要对每个路由单独输出 HTML 文件,配置如下:

js 复制代码
/* .umirc.ts */
export default defineConfig({
    exportStatic: {},
})

Ant Design 5.0 里的日期库自定义问题

我们老项目的日期库使用的 moment.js,而 Ant Design 5.0 默认使用的 Day.js,导致升级之后时间组件报错,在这里可以使用 @ant-design/moment-webpack-plugin 插件,无需对现有代码做任何修改直接替换成 Moment.js,配置如下:

js 复制代码
/* .umirc.ts */
export default defineConfig({
    chainWebpack: (config) => {
        config.plugin('moment-webpack-plugin').use(AntdMomentWebpackPlugin)
        return config
    },
    mfsu: {
        // @ts-ignore
        chainWebpack(config) {
            config.plugin('moment-webpack-plugin').use(AntdMomentWebpackPlugin)
            return config
        },
    },
})

在这里需要注意的是在 mfsu 里也同样要配置 chainWebpack,因为 mfsu 是默认开启的,不配置会导致无法替换成 Moment.js。

Ant Design 5.0 里的组件样式自定义问题

有时候我们希望可以自定义 Ant Design 5.0 里的组件样式,比如图片上传组件 Upload,则可以进行如下配置:

js 复制代码
<Upload
    listType="picture-card"
    accept="image/*"
  >
</Upload>

CSS 文件

css 复制代码
.ant-upload-wrapper.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select {
  width: 375px;
  height: 300px;
}

.ant-upload-wrapper.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select > .ant-upload {
  display: block;
}

考虑到这个样式修改是全局性的,所以建议在这里增加 className="avatar-uploader"(可以是任何值),好处是避免污染全局样式

js 复制代码
<Upload
    listType="picture-card"
    className="avatar-uploader"
    accept="image/*"
  >
</Upload>
css 复制代码
.ant-upload-wrapper.ant-upload-picture-card-wrapper.avatar-uploader .ant-upload.ant-upload-select {
  width: 375px;
  height: 300px;
}

.ant-upload-wrapper.ant-upload-picture-card-wrapper.avatar-uploader .ant-upload.ant-upload-select > .ant-upload {
  display: block;
}

总结

升级之后带来了如下好处:

  1. 降级了升级和维护成本。之前的 Web 应用模板没有任何封装,几乎无法升级,现在则可以通过 umi 进行升级。
  2. 升级之后可以天然享受到对 React Router、Vite、TypeScript、Ant Design、路由数据加载的支持,并且提供的 MFSU 拥有比 Vite 更快的打包速度,将打包速度从 1m8s 秒减少到 17s。

参考

相关推荐
向前看-27 分钟前
验证码机制
前端·后端
燃先生._.1 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖2 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235242 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240253 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar3 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人4 小时前
前端知识补充—CSS
前端·css
GISer_Jing4 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245524 小时前
吉利前端、AI面试
前端·面试·职场和发展