Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor plugins #740

Merged
merged 51 commits into from
Aug 3, 2018
Merged

Refactor plugins #740

merged 51 commits into from
Aug 3, 2018

Conversation

sorrycc
Copy link
Member

@sorrycc sorrycc commented Jul 27, 2018

补充下相比之前有变化的实现方案。

插件解析

插件分:

  • 环境变量引入的插件,env: 前缀
  • 内置插件,built-in: 前缀
  • 用户配置的插件,user: 前缀
  • 插件初始化过程中添加的插件,extra: 前缀

单个插件的格式为:

{
  id,
  apply,
  opts,
}

注册坑位(hooks)

语法:

api.register(key, fn);

实际上是往 service.pluginHooks 注册一个方法,pluginHooks 的格式为:

{
  a: [fn1, fn2],
  b: [fn3, fn4],
}

注册方法(在 api 上)

语法:

api.registerMethod('methodName', {
  type,
  apply,
});

type 和 apply 二选一,type 为 api.API_TYPE.ADD | api.API_TYPE.MODIFY | api.API_TYPE.EVENT 的一项。

比如:

注册方法,

api.registerMethod('addHtmlMeta', { api.API_TYPE_ADD });

调用,

api.addHtmlMeta({ charset: 'utf-8' });

执行插件,

const metas = api.applyPlugins('addHtmlMeta');

背后实现,

api.registerMethod = (hook, { type, apply }) => {
  assert(!api[hook], `api.${hook} exists`);
  assert(type && apply, `Only be one for type and apply.`);
  api[hook] = (...args) => {
    if (apply) { apply(...args) }
    else if (type === api.API_TYPE.ADD) {
      api.register(hook, ({ memo }) => {
        return (memo || []).concat(args[0]);
      });
    } else { ... }
  };
};

配置变更

比如用户配置了,

export default {
  plugins: [
    ['umi-plugin-a', { foo: 'bar' }],
  ],
}

解析为:

{
  id: 'umi-plugin-a',
  apply: 'x',
  opts: { foo: 'bar' },
}

注册到 plugins 里。

初始化时,执行 apply 方法,如果是 dev 模式且有 api.onOptionChange 方法,添加到 plugin 对象里,即:

{
  id: 'umi-plugin-a',
  apply: 'x',
  opts: { foo: 'bar' },
  onOptionChange,
}

默认的 onOptionChange 行为是重新 devServer 。

然后用户修改配置,比如:

export default {
  plugins: [
    ['umi-plugin-a', { bar: 'foo' }],
  ],
}

解析 plugins 配置为 newUserPlugins 列表,和之前的 plugins 过滤出来的 userPlugins 比较,

  • 如果有新增和删除,则重启服务器
  • 如果配置变更(其中 Function 通过 toString() 比较),则覆盖之前的 opts,然后执行 onOptionChange,参数为新的 opts

上面的例子,先设置 opts,

{
  id: 'umi-plugin-a',
  apply: 'x',
  opts: { bar: 'foo' },
  onOptionChange,
}

然后执行 onOptionChange(opts)

额外注册插件及其配置变更

plugins 初始化执行 apply 时,如有 api.registerPlugin,比如:

const pluginId = api.registerPlugin({
  id: 'extra: umi-plugin-dva',
  apply: require('umi-plugin-dva'),
  opts: { immer: true },
});

则添加到 extraPlugins 里。

在 plugins 初始化 apply 完之后,对于 extraPlugins 再执行一遍初始化,初始化时添加到 plugins 里。递归执行,知道没有生成新的 extraPlugins 为止。(递归层级超过 10 级,报错提醒用户可能死循环)

然后检测到配置有变化,对比之前的配置,通过 api.changePluginOption(pluginId, option) 修改插件的 opts 。这会覆盖 plugins 里对应 pluginObject 之前的 opts,然后执行 onOptionChange,参数为新的 opts。

使用举例,

import { diffOptions } from 'umi-utils';

export default (api, option) => {
  const dva = api.registerPlugin({
    apply: require('umi-plugin-dva'),
    opts: { immer: true },
  });

  const pluginsMap = {
    dva,
  };

  api.onOptionChange(newOption => {
    diffOptions(newOption, option).forEach(key => {
      api.changePluginOption(pluginsMap[key], newOption[key]);
    });
    option = newOption;
  });
};

@yutingzhao1991
Copy link
Contributor

api.register 是干啥的?没太明白,确认下,我理解有一个 api.registerMethod 好像就好了。还是说是为了兼容之前的?

api.register('addHtmlMeta', () => {
  return { charset: 'utf-8' };
})

是不是等于:

api.addHtmlMeta({ charset: 'utf-8' });

@sorrycc
Copy link
Member Author

sorrycc commented Jul 30, 2018

api.register() 就是原来的实现,没有变化,api.registerMethod() 是基于 api.register() 的封装,api.register() 可以不在文档上露出。

@sorrycc
Copy link
Member Author

sorrycc commented Jul 31, 2018

整理下 api 接口方法,全部加在 api 上,开发者不需要感知到 service 。

方法

  • register(hook, fn)
  • registerCommand(commandName, opts, fn)
  • registerMethod(name, { type, apply })
  • registerPlugin({ id, apply, opts })
  • applyPlugins(hook, { memo, args })
  • _applyPluginsAsync(hook, { memo, args })
  • onOptionChange(fn)
  • changePluginOption(id, newOpts)
  • addBabelRegister(files)
  • chainWebpackConfig(webpackConfig)
  • modifyAFWebpackOpts(afWebpackOpts => {})
  • _addConfig(config)
  • addPageWatcher(filePath)
  • addEntryImport({ source, specifier })
  • addEntryImportAhead({ source, specifier })
  • addEntryCode(code)
  • addEntryCodeAhead(code)
  • modifyEntryRender()
  • modifyEntryHistory()
  • addRouterImport({ source, specifier })
  • addRouterImportAhead({ source, specifier })
  • modifyRouterRootComponent()
  • onStart()
  • modifyRoutes(routes=> {})
  • onPatchRoute(route => {})
  • onGenerateFiles()
  • addHTMLMeta()
  • addHTMLLink()
  • addHTMLScript()
  • addHTMLHeadScript()
  • modifyDefaultConfig()
  • addRendererWrapperWithModule()
  • addRendererWrapperWithComponent()
  • modifyHTMLWithAST($, { route })

build 时存在的方法

  • onBuildSuccess()
  • onBuildFail()

dev 时存在的方法

  • restart(why)
  • printWarn(messages)
  • printError(messages)
  • refreshBrowser()
  • rebuildTmpFiles()
  • onDevCompileDone({ isFirstCompile, stats })
  • beforeDevServer(({ server }) => {})
  • afterDevServer(({ server }) => {})
  • _beforeServerWithApp(({ app }) => {})
  • addMiddlewareAhead()
  • addMiddleware()

属性

  • id
  • cwd
  • pkg
  • config
  • webpackConfig
  • paths
  • routes
  • API_TYPE.{ADD|MODIFY|EVENT}

utils

  • winPath(filePath)
  • debug(str)
  • findJS(base, filenameWithoutExt)
  • findCSS(base, filenameWithoutExt)
  • relativeToTmp(path)

@sorrycc sorrycc merged commit 14b507b into master Aug 3, 2018
@sorrycc sorrycc deleted the refact/plugin branch August 3, 2018 07:07
@sorrycc sorrycc restored the refact/plugin branch August 3, 2018 07:39
@sorrycc sorrycc deleted the refact/plugin branch August 3, 2018 23:46
@sorrycc sorrycc changed the title WIP: Refactor plugins Refactor plugins Aug 6, 2018
@sorrycc sorrycc mentioned this pull request Aug 6, 2018
32 tasks
sorrycc pushed a commit that referenced this pull request Jun 23, 2022
xierenyuan pushed a commit to xierenyuan/umi that referenced this pull request Jun 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants