将一个老项目的 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。

参考

相关推荐
光影少年1 小时前
react的hooks防抖和节流是怎样做的
前端·javascript·react.js
小毛驴8501 小时前
Vue 路由示例
前端·javascript·vue.js
发现一只大呆瓜2 小时前
AI流式交互:SSE与WebSocket技术选型
前端·javascript·面试
m0_719084113 小时前
React笔记张天禹
前端·笔记·react.js
Ziky学习记录3 小时前
从零到实战:React Router 学习与总结
前端·学习·react.js
wuhen_n3 小时前
JavaScript链表与双向链表实现:理解数组与链表的差异
前端·javascript
wuhen_n3 小时前
JavaScript数据结构深度解析:栈、队列与树的实现与应用
前端·javascript
狗哥哥3 小时前
微前端路由设计方案 & 子应用管理保活
前端·架构
青青家的小灰灰3 小时前
React 19 核心特性与版本优化深度解析
react.js
前端大卫4 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端