• 我把基于react-starter-kit框架的老项目升级到Umi 4.0的经历
  • 发布于 2个月前
  • 196 热度
    0 评论
背景
该项目使用的 Web 应用模板是react-starter-kit,使用的是 2017 年的版本,目前主要遇到如下几个问题:
1.一直没有太大的升级,只对依赖的 React、Webpack、AntDesign 进行必要的升级,升级和维护成本也比较高,并且缺少对 React Router、Vite、TypeScript 的支持,不利于之后的代码维护和新特性的使用。
2.打包性能比较差,目前需要 1 分 08 秒。

升级中遇到的问题
获取 URL 的查询字符串
在 react-starter-kit 中,可以通过 context 获取查询对象,例如:
function action(context) {
  const { query } = context;
  return {
    component: <Admin query={query} />,
  };
}
而在 umi 中,在函数组件里可以通过 useSearchParams 获取,例如:
function Admin() {
    const [searchParams, setSearchParams] = useSearchParams();
    return <div>{searchParams.get('name')}<div>
}
在类组件里可以通过 createSearchParams 获取,例如:
class Admin extends React.Component {
  const searchParams = createSearchParams(window.location.search);
  render() {
    return <div>{searchParams.get('name')}<div>
  }
}
考虑到老项目里都是处理之后通过 props 传递给组件,则可以抽象成一个高阶组件,单独处理获取 URL 的查询字符串的逻辑,这样大部分组件都不需要进行改动,例如:高阶函数 WrappedComponent
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} />
  }
}
// 堆代码 duidaima.com
class Admin extends React.Component {
  render() {
    return <div>{this.props.name}<div>
  }
}
export default WrappedComponent(Admin)
如何配置国际化
Ant Design 里支持使用 ConfigProvider 进行国际化配置,可以放在任何需要国际化的组件中,而我们希望该配置是全局性的,所以在 Umi 中可以配置在 layouts/index.tsx 里,例如:
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 的国际化配置,例如:
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

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


静态部署问题
我们老项目是使用静态站点托管,需要对每个路由单独输出 HTML 文件,配置如下:
/* .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,配置如下:
/* .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,则可以进行如下配置:
<Upload
    listType="picture-card"
    accept="image/*"
  >
</Upload>
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"(可以是任何值),好处是避免污染全局样式
<Upload
    listType="picture-card"
    className="avatar-uploader"
    accept="image/*"
  >
</Upload>

.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。

用户评论