生成器运行时上下文

编写生成器时,最需要理解的概念之一是如何运行方法以及在哪个上下文中运行。

原型方法作为操作

直接附加到生成器原型的每个方法都被视为一项任务。每个任务都由 Yeoman 环境运行循环按顺序运行。

换句话说,Object.getPrototypeOf(Generator) 返回的对象上的每个函数都将自动运行。

辅助方法和私有方法

既然您知道原型方法被视为一项任务,您可能想知道如何定义不会自动调用的辅助方法或私有方法。有三种不同的方法可以实现这一点。

  1. 在方法名称前加上下划线(例如 _private_method)。

       class extends Generator {
         method1() {
           console.log('hey 1');
         }
    
         _private_method() {
           console.log('private hey');
         }
       }
    
  2. 使用实例方法

       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');
           };
         }
       }
    
  3. 扩展父生成器

       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 定义不兼容)

可用的优先级为(按运行顺序)

  1. initializing - 您的初始化方法(检查当前项目状态、获取配置等)
  2. prompting - 您提示用户选择选项的位置(您将在其中调用 this.prompt()
  3. configuring - 保存配置并配置项目(创建 .editorconfig 文件和其他元数据文件)
  4. default - 如果方法名称与优先级不匹配,则将其推入此组。
  5. writing - 您编写生成器特定文件(路由、控制器等)的位置
  6. conflicts - 处理冲突的位置(在内部使用)
  7. install - 运行安装的位置(npm、bower)
  8. end - 最后调用,清理,说再见等

遵循这些优先级指南,您的生成器将与其他生成器很好地配合使用。

异步任务

有多种方法可以暂停运行循环,直到任务完成异步工作。

最简单的方法是**返回一个 Promise**。一旦 Promise 解析,循环将继续,或者如果失败,它将引发异常并停止。

如果您依赖的异步 API 不支持 Promise,那么您可以依赖旧的 this.async() 方法。调用 this.async() 将返回一个函数,在任务完成后调用该函数。例如

asyncTask() {
  var done = this.async();

  getUserEmail(function (err, name) {
    done(err);
  });
}

如果 done 函数以错误参数调用,则运行循环将停止并引发异常。

接下来去哪里?

现在您已经对 Yeoman 的运行上下文有了更多的了解,您可以继续阅读 用户交互