UMI3源码解析系列之运行时插件机制

20eb722d8dec832d541c7a9c64179b5b.png

前言

前面几篇文章,我们分析了umijs「核心」Service「类的初始化流程」「插件化的核心流程」以及「构建阶段」dev「命令的执行流程」。那我们今天继续分析项目在「运行时阶段」又会做哪些事呢?

在开始今天的文章之前,大家不妨想几个问题:

  1. 「自动生成的入口文件」和我们平时自己写的入口文件有什么不一样?

  2. 项目中使用的插件(例如:plugin-modelplugin-request等插件)又是「如何注册到项目」中的?

  3. 在运行时阶段,如何「动态修改路由」或者如何「重写」render「方法」

  4. umijs生成的「临时文件通过其他构建工具」(不限于webpackrollupesbuild)可以跑起来吗?

入口文件

在前面我们提到了在解析preset-built-in预设阶段会批量导入generate files相关的plugin,同时在这些plugin中注册onGenerateFiles钩子,然后在webpack编译前触发,生成临时文件。

接下来,我们从入口文件出发,看下通过执行umi dev命令生成的入口文件内容:

// path: src/.umi/umi.ts

// @ts-nocheck
import './core/polyfill';
import '@@/core/devScripts';
import { plugin } from './core/plugin';
import './core/pluginRegister';
import { createHistory } from './core/history';
import { ApplyPluginsType } from '~/umi-test/node_modules/@umijs/runtime';
import { renderClient } from '~/umi-test/node_modules/@umijs/renderer-react';
import { getRoutes } from './core/routes';

const getClientRender = (args: { hot?: boolean; routes?: any[] } = {}) => plugin.applyPlugins({
  key: 'render',
  type: ApplyPluginsType.compose,
  initialValue: () => {
    const opts = plugin.applyPlugins({
      key: 'modifyClientRenderOpts',
      type: ApplyPluginsType.modify,
      initialValue: {
        routes: args.routes || getRoutes(),
        plugin,
        history: createHistory(args.hot),
        isServer: process.env.__IS_SERVER,
        rootElement: 'root',
        defaultTitle: ``,
      },
    });
    return renderClient(opts);
  },
  args,
});

const clientRender = getClientRender();
export default clientRender();

window.g_umi = {
  version: '3.5.14',
};

// hot module replacement
...

首先可以看到文件的顶部导入了polyfill文件,也就是我们平时自己开发项目导入的babel相关的polyfill文件。

当然在umi-next版本已经在尝试用swc代替babel,感兴趣的小伙伴可以自行查阅umi-next的相关issues

// path: src/.umi/umi.ts

// @ts-nocheck
import 'core-js';
import 'regenerator-runtime/runtime';
export {};

接下来我们继续往下分析,通过执行getClientRender函数,返回clientRender。在getClientRender函数内部我们看到了熟悉的面孔--plugin,运行时阶段同样通过插件化返回渲染需要的render方法。

值得注意的是这里的Plugin是有别于前面提到的PluginAPIPluginAPI是在作用于编译阶段,而Plugin是作用于运行时的插件。

接下来,我们看下运行时插件是怎么实现的。

运行时插件

阅读源码最好的出入点就是从它的测试用例出发。测试用例是题干,源码就是答案。

------加夫列尔·加西亚·马尔波斯

「接下来我们从不同方向出发,更好的了解运行时插件机制的原理及实现。」

从Plugin的测试用例出发

接下来,我们看下几个plugin「测试用例」

  1. 实例化时设置可允许注册的key,同时在register时会校验key是否允许

test('invalid key', () => {
  const p = new Plugin({
    validKeys: [],
  });
  expect(() => {
    p.register({
      apply: { foo: 1 },
      path: '/foo.js',
    });
  }).toThrow(/invalid key foo from plugin \/foo.js/);
});
  1. 通过getHooks方法可获取指定

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值