编写生成器时,最需要理解的概念之一是如何运行方法以及在哪个上下文中运行。
原型方法作为操作
直接附加到生成器原型的每个方法都被视为一项任务。每个任务都由 Yeoman 环境运行循环按顺序运行。
换句话说,Object.getPrototypeOf(Generator)
返回的对象上的每个函数都将自动运行。
辅助方法和私有方法
既然您知道原型方法被视为一项任务,您可能想知道如何定义不会自动调用的辅助方法或私有方法。有三种不同的方法可以实现这一点。
在方法名称前加上下划线(例如
_private_method
)。class extends Generator { method1() { console.log('hey 1'); } _private_method() { console.log('private hey'); } }
使用实例方法
class extends Generator { constructor(args, opts) { // Calling the super constructor is important so our generator is correctly set up super(args, opts) this.helperMethod = function () { console.log('won\'t be called automatically'); }; } }
扩展父生成器
class MyBase extends Generator { helper() { console.log('methods on the parent generator won\'t be called automatically'); } } module.exports = class extends MyBase { exec() { this.helper(); } };
运行循环
如果只有一个生成器,则按顺序运行任务是可以的。但是,一旦您开始将生成器组合在一起,这就不够用了。
这就是 Yeoman 使用**运行循环**的原因。
运行循环是一个支持优先级的队列系统。我们使用 Grouped-queue 模块来处理运行循环。
优先级在您的代码中定义为特殊的原型方法名称。当方法名称与优先级名称相同时,运行循环会将该方法推入此特殊队列。如果方法名称与优先级不匹配,则将其推入 default
组。
在代码中,它将如下所示
class extends Generator {
priorityName() {}
}
您还可以将多个方法分组到一个队列中一起运行,方法是使用哈希而不是单个方法
Generator.extend({
priorityName: {
method() {},
method2() {}
}
});
(请注意,此最后一种技术与 JS class
定义不兼容)
可用的优先级为(按运行顺序)
initializing
- 您的初始化方法(检查当前项目状态、获取配置等)prompting
- 您提示用户选择选项的位置(您将在其中调用this.prompt()
)configuring
- 保存配置并配置项目(创建.editorconfig
文件和其他元数据文件)default
- 如果方法名称与优先级不匹配,则将其推入此组。writing
- 您编写生成器特定文件(路由、控制器等)的位置conflicts
- 处理冲突的位置(在内部使用)install
- 运行安装的位置(npm、bower)end
- 最后调用,清理,说再见等
遵循这些优先级指南,您的生成器将与其他生成器很好地配合使用。
异步任务
有多种方法可以暂停运行循环,直到任务完成异步工作。
最简单的方法是**返回一个 Promise**。一旦 Promise 解析,循环将继续,或者如果失败,它将引发异常并停止。
如果您依赖的异步 API 不支持 Promise,那么您可以依赖旧的 this.async()
方法。调用 this.async()
将返回一个函数,在任务完成后调用该函数。例如
asyncTask() {
var done = this.async();
getUserEmail(function (err, name) {
done(err);
});
}
如果 done
函数以错误参数调用,则运行循环将停止并引发异常。
接下来去哪里?
现在您已经对 Yeoman 的运行上下文有了更多的了解,您可以继续阅读 用户交互。