真实项目umi3升级umi4实战

4,068 阅读4分钟

umi升级到v4,早就想着升级一波手里的项目了,2022年末一堆事,2023开始终于有时间开搞了。

根据官网升级指南

umijs.org/docs/introd…

项目环境: 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 中取出image

下面我们结合实际项目一起看看

刚 pnpm start 启动项目我就遇到了一个错误,提示我们:绝对子路由路径必须以其所有父路由的组合路径开始。

image.png

按提示修复完后,又看到我们的项目只有一个layout框架没有内容,这就涉及到路由 children 的变更

image.png

修改 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 插件。 image.png 修改 .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,
};

目前我们可以正常发起请求进行鉴权了,因为我们还未登录所以重新重定向到登录页,但是出现如下错误

image.png

这是因为在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声明

image.png