背景
该项目使用的 Web 应用模板是react-starter-kit,使用的是 2017 年的版本,目前主要遇到如下几个问题:
- 一直没有太大的升级,只对依赖的 React、Webpack、AntDesign 进行必要的升级,升级和维护成本也比较高,并且缺少对 React Router、Vite、TypeScript 的支持,不利于之后的代码维护和新特性的使用。
- 打包性能比较差,目前需要 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;
}
总结
升级之后带来了如下好处:
- 降级了升级和维护成本。之前的 Web 应用模板没有任何封装,几乎无法升级,现在则可以通过 umi 进行升级。
- 升级之后可以天然享受到对 React Router、Vite、TypeScript、Ant Design、路由数据加载的支持,并且提供的 MFSU 拥有比 Vite 更快的打包速度,将打包速度从 1m8s 秒减少到 17s。