1

Many projects at work are developed based on umi, so I recently learned the source code of umi, and I have a little more goodwill about this framework~. If you are also interested, welcome to learn or review with me.

This article will take you from the start of project operation, step by step to understand the core part of umi.


After we create the umi project, the first step is generally to use the yarn start command to run it. The execution is umi dev , which is the umi command, so let's first see how the umi command is defined.

下面提到的源码目录在 umi 的源码仓库 /packages 目录下。

umi command

The umi command is defined in the /umi/bin directory and executes /umi/src/cli.ts by default. The logic is as follows:

image.png

1. Parameter normalization

Use the yargs-parser library to handle command line arguments, and the logic for handling version , help commands.

2. [dev] Start a new process

Let's talk about the difference between dev and build . The development environment will additionally start a new process to run the service, and do some event monitoring and communication processing.

This part of the code is in /umi/src/utils/fork.ts , and the core logic mainly consists of three parts:

##### 2.1 Handling port numbers

The default port is 8000, if it is occupied, it will automatically increase by 1.

##### 2.2 Start a new process

Use child_process of fork to create a new child process and execute /umi/src/forkedDev.ts . The logic in this file is the third and fourth steps below.

##### 2.3 Handling Communication

After the child process is created, the events of the child process will be monitored to deliver messages. Two events will be processed here: RESTART restart and UPDATE_PORT update port.

The main process (the current process running the umi command) will listen for events such as exit to kill the child process and trigger the onExit event of the plugin.

3. Initialize webpack

This part of the code is at /umi/src/initWebpack.ts .

Before initialization, it will go to the configuration file to find out if there is a configuration of webpack5 or mfsu . If there is, initialize webpack5, if not, initialize webpack4.

An important point to mention here is that umi encapsulates dependencies similar to webpack in deps . The previous "Umi 4 Design Ideas" speech mentioned the concept of middlemen, and umi will do some pre-packaged work to solve version stability. sexual issues.

4. Construct the Service object

Finally talking about the core part of umi, the code is short but important.

await new Service({ ...params }).run({
  name: 'dev', // or 'build'
  args,
});

There is a special treatment here. The Service used for initialization is a small package, and preset-built-in is built in by default. This preset is the implementation part of the extension method of the plugin.

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

class Service extends CoreService {
  constructor(opts: IServiceOpts) {
    ...

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

Plugin is a very important part of umi's design. The idea of plug-in allows umi to easily control the process and implement customization. This idea has an academic name microkernel architecture.

microkernel architecture

This part will not be discussed. I also checked the data and picked up some important points, focusing on understanding the design ideas of the core system. The Service class is the core system of umi.

image.png

Core class Service

The code is in /core/src/Service/Service.ts . The structure of this class is very simple, with only two parts: the constructor and run() method.

constructor constructor

image.png

The main work of the initialization phase is to collect the configuration. From this, we can see that the author is so careful, the priority of the environment variables, the priority of the configuration file, and even the details of the page(s) directory name are clearly arranged.

The initialized attributes can be clearly seen from the figure, so I won't go into details. Only the important parts (I think) are listed here.

When initializing presets and plugins, the information of all plugins will be recorded, that is, the plugin registry, so as to facilitate the management and operation of plugins.

run()

run~

image.png

To sum it up, this part is going forward by life cycle:

  1. Initialize presets and plugins
  2. set some hooks
  3. Finally, execute runCommand() run the specific logic related to the umi command

Service has 9 life cycles, which are used as the basis for judging some actions performed in subsequent plug-ins.

export enum ServiceStage {
  uninitialized,
  constructor,
  init,
  initPresets,
  initPlugins,
  initHooks,
  pluginReady,
  getConfig,
  getPaths,
  run,
}

The initialization logic of presets and plugins is the same, and a few important lines of pseudocode are excerpted as follows:

const api = this.getPluginAPI({ id, key, service: this })
// 获取 PluginAPI 对象,用来传递给 plugin 本身,也就是插件的实现规范

this.registerPlugin(preset)
// 插件注册表,执行的代码是 this.plugins[preset.id] = preset

const { presets, plugins } = await this.applyAPI({ api, apply: preset.apply })
// 执行插件内部的 apply 方法,即 return apply()(api)
PluginAPI

This class defines the core method of the Plugin, which has been described in detail in the umi documentation.

image.png

applyPlugin()

The core method of plug-in execution, type in the parameter determines the logic of executing the plug-in, add and modify will add or modify the initialValue, and return the result after running, event exists as an event as the name suggests.

tapable(webpack) I am still learning and summarizing, there are excellent articles welcome to recommend to me~.

image.png

runCommand()

Here we mainly look at the logic related to the dev command, the code is in preset-built-in/src/plugins/commands/dev/dev.ts .

image.png

preset-built-in

image.png

This is the default plug-in set of umi, and the functions implemented are mainly divided into five parts:

  1. registorMethods

    Unified registration method.

  2. route

    Implementation of routes configuration.

  3. write temporary file

    The file generation process in the src/.umi directory is all here, including the entry, routing, plug-ins, etc. of the project.

  4. configure

    umi's document configures the implementation in .

  5. commands

    The specific implementation logic of the command and the modification of the webpack configuration are related.


Just share it here, please correct me if I am wrong.

refer to

"Ant Front-end R&D Best Practices" Text

"Umi 4 Design Ideas - Yun Qian" video & text version

"umi source code" know the column

umi source code-curtain


Jing
156 声望18 粉丝