umi升级到v4,早就想着升级一波手里的项目了,2022年末一堆事,2023开始终于有时间开搞了。
根据官网升级指南
项目环境: macOS M2 Pro, umi: ^3.5.24, node: v14.17.5, pnpm: ^7.26.3
第一步 依赖处理
{
"devDependencies": {
+ "@umijs/max": "^4.0.0",
- "umi": "^3.0.0",
- "@umijs/preset-react": "^1.2.2"
}
}
更新完 package.json 然后删除 node_modules 包,最后运行 pnpm(官方推荐使用pnpm) install 更新依赖
如遇到以下错误,那是因为启动命令我们还没有修改,下面进行第二步 启动命令
> @0.0.1 postinstall /Users/wyg/SZSK/DP_Lab/maintenance_center_front
> umi generate tmp
sh: umi: command not found
ELIFECYCLE Command failed.
第二步 启动命令
{
"scripts": {
- "build": "umi build",
+ "build": "max build",
- "postinstall": "umi g tmp",
+ "postinstall": "max setup",
- "start": "umi dev",
+ "start": "max dev",
}
}
遇到以下错误,说明我们的配置文件 .umirc.ts 或者config 文件 中的 fastRefresh 属性类型不对,下面进行第三步 配置层迁移
> @0.0.1 postinstall /Users/wyg/SZSK/DP_Lab/maintenance_center_front
> max setup
fatal - AssertionError [ERR_ASSERTION]: Invalid config values: fastRefresh
Invalid value for fastRefresh:
"value" must be a boolean
at Function.validateConfig (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/@umijs+core@4.0.50/node_modules/@umijs/core/dist/config/config.js:188:31)
at Config.getConfig (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/@umijs+core@4.0.50/node_modules/@umijs/core/dist/config/config.js:63:12)
at Service.resolveConfig (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/@umijs+core@4.0.50/node_modules/@umijs/core/dist/service/service.js:324:55)
at Service.run (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/@umijs+core@4.0.50/node_modules/@umijs/core/dist/service/service.js:269:50)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Service.run2 (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/umi@4.0.50_2ytp36kpei5tia7ihg32kn6chi/node_modules/umi/dist/service/service.js:61:12)
at async run (/Users/wyg/SZSK/DP_Lab/maintenance_center_front/node_modules/.pnpm/umi@4.0.50_2ytp36kpei5tia7ihg32kn6chi/node_modules/umi/dist/cli/cli.js:55:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '=='
}
第三步 配置层迁移
import { defineConfig, utils } from 'umi';
export default defineConfig({
model: {}
antd: {},
request: {},
initialState: {},
mock: {
include: ['src/pages/**/_mock.ts'],
},
dva: {},
layout: {
// https://umijs.org/docs/max/layout-menu#构建时配置
title: 'UmiJS',
locale: true,
},
// https://umijs.org/zh-CN/plugins/plugin-locale
locale: {
// default zh-CN
default: 'zh-CN',
antd: true,
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
},
});
存在差异的配置项如下 config/config.ts :
import { defineConfig, utils } from 'umi';
export default defineConfig({
- fastRefresh: {},
+ fastRefresh: true,
dva: {
// 不再支持 hmr 这个参数
- hmr: true,
},
// 默认 webpack5
- webpack5: {},
})
差异不止这些,还有一些我们自己配置的优化方案,这两项也要删除掉,不然运行会报错
cssModulesTypescriptLoader: {
mode: 'emit',
},
nodeModulesTransform: {
type: 'none',
},
然后我们就可以看是安装依赖了 pnpm install
如果出现以下类型的错误,则说明我们没有明确的安装所示的依赖包,moment 在 umi3之间都是默认跟着umi 一起下载安装的,但是umi4好像并没有一起安装,所以我们要在 dependencies 依赖中明确安装
了解更多 pnpm、yarn、npm 区别,传送门
error - Can not resolve dependence : 'moment', please install it
error - AssertionError [ERR_ASSERTION]: dependence not found: moment
依赖下载完成,我们接下来就开始最重要的一步了,代码层修改
第四步 代码层修改
代码层修改,最重要的就是路由了,由于router的升级许多老的API不再适用
这是我们大多数开发者痛恨的,以下引用官方描述
Umi 4 中将 react-router@5 升级到 react-router@6,所以路由相关的一些 api 存在着使用上的差异。
props 默认为空对象,以下属性都不能直接从 props 中取出
下面我们结合实际项目一起看看
刚 pnpm start 启动项目我就遇到了一个错误,提示我们:绝对子路由路径必须以其所有父路由的组合路径开始。
按提示修复完后,又看到我们的项目只有一个layout框架没有内容,这就涉及到路由 children 的变更
修改 layouts/index.tsx:
import React from 'react';
+ import { Outlet } from 'umi';
export default function Layout(props) {
return (
<div>
- { props.children }
+ <Outlet />
</div>
);
}
修改后出现下图layout出现两次的情况,出现的原因:框架自带全局 layout, 即便我们在 .umirc.ts 配置文件中 layout:false,这里的配置仅是关闭 layout 插件。
修改 .umirc.ts
// .umirc.ts
export default {
routes: [
{
path: '/login',
component: 'login',
+ layout: false,
},
{
path: '/',
component: '@/layouts/index',
routes: [
{ path: '/list', component: 'list' },
{ path: '/admin', component: 'admin' },
],
+ layout: false,
},
],
}
保存页面就可以看到正常的layout了
如上图看到的,我们的页面只出现了一个骨架框,因为我们用到了鉴权,/wrappers/index.tsx,里同样需要把
props.children替换成<Outlet />
有鉴权自然就会涉及到网络请求,我们利用 module 的 subscriptions.setup 函数初始化请求,之前利用history.listen 监听路由变化,升级后路由变化监听不到了,所以做出如下修改
export default {
namespace: 'user',
state: {},
subscriptions: {
setup({ dispatch, history }) {
- return history.listen(() => {
const pathname = history.location.pathname;
dispatch({ type: 'getEnvironment' });
if (!pathname.includes('login')) {
dispatch({ type: 'checkLogin' });
}
- });
},,
effects,
reducers,
};
目前我们可以正常发起请求进行鉴权了,因为我们还未登录所以重新重定向到登录页,但是出现如下错误
这是因为在umi4中,location.query api已被遗弃,我们需要改用 location.search 并配合 query-string,项目中所有用到 location.query 都需要替换成 location.search;
还有使用history.push({ pathname: '/path', query: { id: 123 }}); 也需要全部替换成 history.push('/path?id=123')
同时 history.goBack也被废弃了,改用 history.back
我们之前用的
{ exact: true, path: '/', redirect: '/home' }方式进行路由重定向,也不起作用了,需要做如下调整
配置路由重定向没有问题,是我配置的不对,需要把重定向配置放到对应的路由同级
layouts/index.tsx:
import React from 'react';
import { Outlet, useLocation, Navigate } from 'umi';
export default function Layout(props) {
const location = useLocation();
if (location.pathname === '/') {
return <Navigate to="/workbench" />;
}
return (
<div>
<Outlet />
</div>
);
}
通常情况下我们不需要再
package.json中声明antd等包,这些umijs 都会自动加载依赖,不过eslint可能会报错如图,我们需要把这些报错的依赖在package.json声明