010 使用 Umi 配置,定制化你自己的 Umi 框架

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

一般你会在官网上看到 Umi 的配置分为普通配置和运行时配置。在本文中我们都会详细说明,并且还会聊一聊其他的官网没有明说的配置。

普通配置是在构建时读取的,你可以理解为传给 webpack 的配置。(当然实际上,它最终也给传给了其他的工具或者组件)这意味着它是运行在 node 环境中的,所以你可以在普通配置文件中,去使用一些 node 的 api.

而运行时配置时是跑在浏览器端的,这意味着你可以在运行时配置的文件中写函数、jsx还有其他的一些浏览器端的依赖。注意在这里使用 node 的 api 会导致程序崩溃。

配置

Umi 的配置文件两个 .umirc.tsconfig/config.ts

Umi 中约定的文件都支持 ts 和 js。你可以根据自己项目中使用的语言做出合理的选择,因为这个课程推荐全程使用 typescript 开发,所以在说明的时候,我会直接写明文件后缀是 ts|tsx

.umirc.ts

与 config/config.ts 文件功能相同,2 选 1 。.umirc.ts 文件优先级较高

配置文件,包含 Umi 内置功能和插件的配置。

config/config.ts

与 .umirc.ts 文件功能相同,2 选 1 。.umirc.ts 文件优先级较高

配置文件,包含 Umi 内置功能和插件的配置。

配置文件实践

新建 config/config.ts

export default {

};
复制代码

为了获得更好的开发体验,我们引入 Umi 导出的配置文件定义方法 defineConfig

import { defineConfig } from "umi";

export default defineConfig({

});
复制代码

以上两种写法,在功能上是没有区别的,但是有个好处是,我们在编写配置项的时候,会有“联想”推荐和类型校验,比如,你输入一个字母 t,就会自动联想推荐 Umi 支持的所有配置,这个配置项还会根据你引用的插件数量而发生变化。

import { defineConfig } from "umi";

export default defineConfig({
   t
   // targets
   // (property) targets?: {
   //  [key: string]: any;
   // } | undefined

   // terminal
   // (property) IConfigFromPlugins.terminal?: {} | undefined

   // theme
   // (property) IConfigFromPlugins.theme?: {} | undefined

   // title
   // (property) IConfigFromPlugins.title?: string | undefined

   // tmpFiles
   // (property) IConfigFromPlugins.tmpFiles?: boolean | undefined
});
复制代码

并且在你的值写错时 VS Code 就会直接给出标红提示,而不用等到运行构建时,才能暴露问题。这在开发前期你不熟悉 Umi 框架的时候,将会非常有用。在你觉得某个功能没有按照预期表现时,你可以很好的排查你的配置没有写错。

import { defineConfig } from "umi";

export default defineConfig({
   title:[123]
   // 此处标红,鼠标移上去,会有详细的错误说明
   // 不能将类型“number[]”分配给类型“string”。ts(2322)
   // pluginConfig.d.ts(9, 1): 所需类型来自属性 "title",在此处的 "ConfigType" 类型上声明该属性
});
复制代码

将上面的 title 配置修改为正确的 string 类型:"Hello Umi"。

运行项目(pnpm start),你将在浏览器中看到也看的 title 从 http://127.0.0.1:8000/ 变成了 Hello Umi

其他的配置,我们将会在后续用到的时候逐个说明。

运行时配置

运行时配置,光从字面上理解,那就是为框架提供一些动态的配置数据,简单的概括成一句废话就是运行时配置是运行时使用的配置。

这意味着其实我们可以在这里面执行一些异步的 Effect。

首先运行时里面支持的所有配置和普通配置一样,都是有 Umi 插件扩展而来的。所以如果你遇到在使用一个配置时,Umi 告诉你这个配置不存在,那最大的可能就是你少用了某个插件。

Umi 的运行时配置文件只有一个 src/app.ts|x

我们以 render 为例来简要说明,感受运行时配置的特性。

新建 src/app.ts

export function render(oldRender: any) {
  fetch("/api/auth").then((auth) => {
    if (auth.isLogin) {
      oldRender();
    } else {
      history.push("/login");
      oldRender();
    }
  });
}
复制代码

render 是一个函数,会传入旧的页面 render 函数。最简单的理解这个配置,你可以把它当作一种阻断页面渲染的手段,就是当你配置了 render 你就暂停了页面的渲染,当你执行完所有你需要的代码逻辑之后,你调用 oldRender 就可以让页面继续渲染了。

如上述的伪代码中,我们先请求了 /api/auth 接口,检测用户是否已登录,如果已登录(isLogin)就继续渲染页面,如果未登录,就跳转到登录页面("/login")。

在这里你可以执行任意的逻辑,甚至如果你觉得你的页面白屏(或者loading)时间太短,你可以在这里倒数三秒再开始渲染页面。

export function render(oldRender: any) {
  setTimeout(() => {
    oldRender();
  }, 3000);
}
复制代码

重新访问开发服务,刷新页面,你需要等待至少 3 秒,才能看到页面被正确渲染。

其他配置

除了官网上提到的普通配置和运行配置,其实我们在使用 Umi 开发项目的时候,还会用到一些其他的配置,比如环境变量,或者在插件中配置等手段,这些内容在官网的文档中都有,但是并不是被归类到 Umi 的配置中。

环境变量

定义环境变量有两个地方,一个是 .env 文件

新建 .env

PORT=8888
复制代码

运行开发服务,服务端口号被正确修改为 8888

ready - App listening at http://127.0.0.1:8888
复制代码

另一个地方是启动 umi 命令的时候,一起传入.

如在 package.json 中配置 ANALYZE=1

{
  "name": "umi4-course",
  "scripts": {
    "start": "umi dev",
    "build": "ANALYZE=1 umi build"
  },
}
复制代码

执行 pnpm build,你可以在项目构建介绍,访问 http://127.0.0.1:8888 查看构建产物中都包含哪些依赖。

将构建产物添加到 gitignore 中,增加 dist

需要注意的是,在执行命令时配置环境变量,有平台的差异。

# OS X, Linux
$ PORT=3000 umi dev
 
# Windows (cmd.exe)
$ set PORT=3000&&umi dev
复制代码

一般我们都使用 cross-env 来消除平台差异

$ pnpm install cross-env -D
$ cross-env PORT=3000 umi dev
复制代码
{
  "name": "umi4-course",
  "scripts": {
    "start": "umi dev",
    "build": "cross-env ANALYZE=1 umi build"
  },
}
复制代码

插件中配置 Umi

在插件开发中使用 modifyConfig 修改用户的配置,这是我最喜欢用的一种方式,因为可以“黑盒”的修改一些 Umi 的配置,其实也相当于修改了 Umi 的默认行为,比如在同一个团队中,我们可以将一些共同的配置,放到插件中,这样每一个项目的配置文件就会非常的干净。同团队中,会大大减少配置文件的维护工作。

像 alita 中,使用 modifyConfig 修改了 alita 项目中默认的一些配置信息,这样 alita 项目中的配置文件就可以很干净。加入说我们不使用 modifyConfig,而是要求用户严格编写如下配置,才能正常使用。这无疑会大大的增加框架的维护成本。

  const configDefaults: Record<string, any> = {
    history: { type: 'hash' },
    title: false, // 默认内置了 Helmet
    targets: {
      ie: 9,
    },
    hash: true,
    hd: api.userConfig.appType !== 'pc' ? {} : false,
    dva: {
      enableModelsReExport: true,
    },
    model: {},
    request: {},
    displayName: 'alita-demo',
    conventionRoutes: {
      // 规定只有index文件会被识别成路由
      exclude: [
        /(?<!(index|\[index\]|404)(\.(js|jsx|ts|tsx)))$/,
        /model\.(j|t)sx?$/,
        /\.test\.(j|t)sx?$/,
        /service\.(j|t)sx?$/,
        /models\//,
        /components\//,
        /services\//,
      ],
    },
    ...api.userConfig,
  };
  api.modifyConfig((memo: any) => {
    Object.keys(configDefaults).forEach((key) => {
      memo[key] = configDefaults[key];
    });
    return memo;
  });
复制代码

在插件开发中设置环境变量会非常的简单,直接将环境变量设置成对应的值即可。

编写项目中的插件

新建 plugin.ts

存在 plugin.ts 这个文件,会被当前项目加载为 Umi 插件,这属于 Umi 的约定行为,你可以在这里解一些需要插件级支撑的问题。出了默认加载这个插件,你也可以使用 plugins 配置,引入项目中的其他相对路径的插件文件。

import { IApi } from "umi";

export default (api: IApi) => {
  // 通过插件设置环境变量
  process.env.COMPRESS = "none";

  // 通过插件修改配置
  api.modifyConfig((memo: any) => {
    memo.title = "Hello Umi";
    return memo;
  });
};
复制代码

通过上面的插件,我们定义了一个环境变量 COMPRESS = "none",它的作用时在执行 umi build 构建的时候不压缩代码,这在定位线上 bug 的时候非常的有用。当然我们这里只是演示,真实的项目中,肯定不能让这个构建一直保持不压缩,而是应该在线上出现 bug 时,临时使用一次这个变量,编译一次项目。

我们还将用户的配置 title 修改成了 "Hello Umi",这样我们就可以删掉本地配置文件中的 title 配置。看起来就像是 Umi 默认的 title 就是 Hello Umi。当然我们也可以在这里写上其他的配置,修改 Umi 的其他默认行为,定制化属于自己的一个 Umi 框架。

源码归档

猜你喜欢

转载自juejin.im/post/7106847783860224008
今日推荐