umi源码阅读1:启动过程

1,193 阅读3分钟

umi是插件化的企业级前端应用框架。作为前后端分离的的前端部分开发框架,集成了常用的一些开发技术,是一种比较优秀的实践方案。主要用于支撑react的开发,4.x以上开发了支撑其他开发语言的入口。

umi阅读分为5个部分,分别是:

以下代码以umi的3.5.20版本为例,主要内容以源码+个人解读为主

启动过程

内容:以umi dev命令启动,走通umi编译代码的流程,寻找umi从dev命令开始,进行了那些操作,到最后执行了真正的编译操作。

umi源码是多包管理,核心入口库是umi。

umi提供了umi dev来启动项目,而命令的注册来自bin,找到packages/umi/bin文件夹

const { name, bin } = require('../package.json');
const localCLI = resolveCwd.silent(`${name}/${bin['umi']}`);
if (!process.env.USE_GLOBAL_UMI && localCLI && localCLI !== __filename) {
  const debug = require('@umijs/utils').createDebug('umi:cli');
  debug('Using local install of umi');
  require(localCLI);
} else {
  require('../lib/cli');
}

该文件又加载了../lib/cli,该路径是编译后的压缩路径,所以应该找到packages/umi/src/cli.ts

该文件主要是处理命令行参数和对命令做出反应,核心代码:

 switch (args._[0]) {
      case 'dev':
        const child = fork({
          scriptPath: require.resolve('./forkedDev'),
        });
        //...
        break;
      default:
        const name = args._[0];
        if (name === 'build') {
          process.env.NODE_ENV = 'production';
        }

        // Init webpack version determination and require hook for build command
        initWebpack();

        await new Service({
          cwd: getCwd(),
          pkg: getPkg(process.cwd()),
        }).run({
          name,
          args,
        });
        break;
    }

先看dev命令,加载了forkedDev文件,我们找到packages/umi/src/forkedDev.ts

import { Service } from './ServiceWithBuiltIn';

try {
    process.env.NODE_ENV = 'development';
    // Init webpack version determination and require hook
    initWebpack();

    const service = new Service({
      cwd: getCwd(),
      pkg: getPkg(process.cwd()),
    });
    await service.run({
      name: 'dev',
      args,
    });

     let closed = false;
    // kill(2) Ctrl-C
    process.once('SIGINT', () => onSignal('SIGINT'));
    // kill(3) Ctrl-\
    process.once('SIGQUIT', () => onSignal('SIGQUIT'));
    // kill(15) default
    process.once('SIGTERM', () => onSignal('SIGTERM'));

    function onSignal(signal: string) {
      if (closed) return;
      closed = true;

      // 退出时触发插件中的onExit事件
      service.applyPlugins({
        key: 'onExit',
        type: service.ApplyPluginsType.event,
        args: {
          signal,
        },
      });
      process.exit(0);
    }
  } catch (e) {
    console.error(chalk.red(e.message));
    console.error(e.stack);
    process.exit(1);
  }

主要的代码时初始化了webpack和新建了一个Service实例并执行service.run()。同时监听了结束线程的操作并使用service实例来结束线程。这么看来Service就是核心了。

接下来看看Service对象,继续跳转到./ServiceWithBuiltIn(内置服务)

import { IServiceOpts, Service as CoreService } from '@umijs/core';

class Service extends CoreService {
  constructor(opts: IServiceOpts) {
    process.env.UMI_VERSION = require('../package').version;
    process.env.UMI_DIR = dirname(require.resolve('../package'));

    super({
      ...opts,
      presets: [
        require.resolve('@umijs/preset-built-in'),
        ...(opts.presets || []),
      ],
      plugins: [require.resolve('./plugins/umiAlias'), ...(opts.plugins || [])],
    });
  }
}

Service继承了CoreService,在实例化的同时加载了@umijs/preset-built-inpresets,和./plugins/umiAliasplugins。

plugins:配置额外的 umi 插件。

presets: 同 plugins 配置,用于配置额外的 umi 插件集。

通俗来讲,都是umi提供的插件机制的不同表现而已,可以把两者当成一种来看,初始化都是对应的绝对路径数组

其他重点文件解读

packages/umi/src/defineConfig.ts

import { IConfigFromPlugins } from '@@/core/pluginConfig';
import { IConfig } from '@umijs/types';

// IConfig types is prior to IConfigFromPlugins in the same key.
export function defineConfig(
  config: IConfigFromPlugins | IConfig,
): IConfigFromPlugins | IConfig {
  return config;
}

该文件定了defineConfig函数,用于去定义一些自定义配置,它直接返回了config对象,并提供了配置参数的类型提示。我们知道config是service需要用到,那Service怎么拿到config的呢,我们后续继续跟踪这个问题。

下一篇:umi源码阅读2:实例化过程

参考资料:

UMI3源码解析系列之构建原理_大转转FE的博客-CSDN博客

umi源码解析

if 我是前端Leader,谈谈前端框架体系建设 - 掘金