yeoman-generator入门教程

hubuke 8年前
   <h2>1. yeoman</h2>    <p><a href="/misc/goto?guid=4958524933186115738" rel="nofollow">Yeoman</a>可以帮助我们创建新的开发项目,为我们提供更好的工具来使我们的项目更多样化。</p>    <h2>2. yeoman-generator</h2>    <p><a href="/misc/goto?guid=4959664199845550928" rel="nofollow">Generators</a>是yeoman生态系统的积木,是通过yo命令运行而为终端用户生产文件的插件。</p>    <h2>3. 构建创建自己的generator</h2>    <h3>1. 创建 node module</h3>    <p>在你需要创建generator的地方创建一个目录,目录要以generator-name命名(name替换成你要创建的generator名 称)。命名规则很重要,因为yeoman会通过文件系统来查找可以使用的generator。 在上面创建的目录下创建一个package.json文件,该文件是一个Node.js项目的的模块清单.相关介绍请看<a href="/misc/goto?guid=4959655379072329693" rel="nofollow">官网</a>。你可以通过在命令行运行 npm init 来构建package.json文件,当然你也可以手动填充这个文件:</p>    <pre>  <code class="language-javascript">  {        "name": "generator-name",        "version": "0.1.0",        "description": "",        "files": [          "app",          "router"        ],        "keywords": ["yeoman-generator"],        "dependencies": {          "yeoman-generator": "^0.20.2"        }      }</code></pre>    <p> </p>    <p>其中 name 属性必须以generator-前缀。keywords 属性必须包含”yeoman-generator” 并且repo必须有一个引用<a href="/misc/goto?guid=4959664198984160869" rel="nofollow"> generators page</a>索引的描述。</p>    <p> </p>    <p>你必须保证引用了最新版本的yeoman-generator 模块作为依赖.可以通过 npm install –save yeoman-generator来实现。</p>    <h3>2.目录树</h3>    <p> </p>    <pre>  <code class="language-bash">yeoman是深度依赖你组织的文件目录文件系统,每一个子generator都有自己的文件夹。      generator默认把app作为默认的子generator,当你使用```yo name``` 调用的是app子generator。因此你的generator必须包含app/目录。你可以通过```yo name:subcommand```命令来调用子generator,他会调用与subcommand完全一样的文件夹即子generator。      下面是示例项目的文件夹结构    ```javascript      ├───package.json    ├───app/    │   └───index.js    └───router/         └───index.js</code></pre>    <p> </p>    <p>通过命令 yo name 和 yo name:router 可以显示出来generator。</p>    <p> </p>    <p>如果你不想把所有的文件都放在根目录下面,很幸运的是yeoman支持两种不同的目录结构./ 和 generators/ ,yeoman会从他们当中注册可用的generators。</p>    <p>所以前一个示例的结构也可用定义成如下:</p>    <pre>  <code class="language-bash">    ├───package.json        └───generators/            ├───app/            │   └───index.js            └───router/                └───index.js</code></pre>    <p> </p>    <p>如果你使用这种目录结构,请确保package.json 文件内的 files 属性定义了子generator在generators目录下:</p>    <p> </p>    <pre>  <code class="language-javascript">     {           "files": [             "generators/app",             "generators/router"           ]         }</code></pre>    <p> </p>    <p> </p>    <p> </p>    <h3>3. 扩展generator</h3>    <p>当到了这一步你就可用去实现generator的具体内容了</p>    <p>yeoman提供了一个基础的generator让我们扩展使用以实现自己的目标.基础的generator提供了大部分的功能来缓解你的任务量。</p>    <p>下面是扩展基础generator的方法:</p>    <pre>  <code class="language-javascript">    var generators = require('yeoman-generator');        module.exports = generators.Base.extend();</code></pre>    <p> </p>    <p>extend 允许你在基础的class上扩展新的自己想要的prototype规范.他的功能是通过<a href="/misc/goto?guid=4959664199123579841" rel="nofollow">Class-extend</a>模块得来的。如果你用过backbone,你会觉得他很熟悉。在生态系统中,我们指定扩展的generator使用module.export来让其有效,就像<a href="/misc/goto?guid=4959676939413776464" rel="nofollow">在nodeJs使用export module一样</a></p>    <p> </p>    <h3>4. 重写constructor方法</h3>    <p>有些generator方法只有定义在构造方法内才能被调用到.这些特殊的方法可以做的一些重要的操作等,而这些操作可能在构造之外无法正常运行。</p>    <pre>  <code class="language-javascript">    module.exports = generators.Base.extend({          // The name `constructor` is important here          constructor: function () {            // Calling the super constructor is important so our generator is correctly set up            generators.Base.apply(this, arguments);              // Next, add your custom code            this.option('coffee'); // This method adds support for a `--coffee` flag          }        });</code></pre>    <p> </p>    <p> </p>    <p> </p>    <h3>5. 添加自定义方法</h3>    <p>当generator被调用时,通常情况下里面定义的方法会按照顺序执行,但是我们将在下一节中看到的,一些特殊的方法名称将触发特定的运行秩序。</p>    <p>下面的一段代码是创建两个自定义的方法</p>    <pre>  <code class="language-javascript">    module.exports = generators.Base.extend({          method1: function () {            console.log('method 1 just ran');          },          method2: function () {            console.log('method 2 just ran');          }        });</code></pre>    <p> </p>    <p>当运行generator 你会看到会用log打印出来。</p>    <p> </p>    <h3>6. 运行generator</h3>    <p>当在本地目录内完成上面的创建后,generator还不能被当做全局的npm module ,我们可以通过在generator-name/目录下运行 npm link 来实现generator的全局化。</p>    <h3>7.找到工程根目录</h3>    <p>当使用yo命令来运行generator的生活,yeoman会把 .yo-rc.json文件所在的目录作为工程的根目录,之后Yeoman将当前文件目录跳转到根目录下运行请求的生成器。当我们使用 this.config.save()的时候,storage模块会创建它。如果.yo-rc.json 不在当前的工作目录,请确保他也不在其他的项目目录里。</p>    <h2>4. 完善generator</h2>    <p>### 1.创建用户交互</p>    <p>我们推荐在prompting 方法内来定义prompt与用户交互,定义方法如下:</p>    <pre>  <code class="language-javascript">   module.exports = generators.Base.extend({         prompting: function () {           return this.prompt([{             type    : 'input',             name    : 'name',             message : 'Your project name',             default : this.appname // Default to current folder name           }, {             type    : 'confirm',             name    : 'cool',             message : 'Would you like to enable the Cool feature?'           }]).then(function (answers) {             this.log('app name', answers.name);             this.log('cool feature', answers.cool);           }.bind(this));         }       })</code></pre>    <h3>2. 运行上下文</h3>    <p>在generator内,所有的静态方法都会被作为action而自定执行,当然generator也提供了可以声明不自动执行的辅助函数,generator提供了三种可以创建辅助函数的方法.</p>    <p>1 通过下划线开头定义函数,如:CopyFiles</p>    <p>2 使用实例函数声明:</p>    <pre>  <code class="language-javascript">    generators.Base.extend({            constructor: function () {              this.helperMethod = function () {                console.log('won\'t be called automatically');              };            }          });</code></pre>    <p>3 继承一个父级generator:</p>    <pre>  <code class="language-javascript">      var MyBase = generators.Base.extend({            helper: function () {              console.log('methods on the parent generator won\'t be called automatically');            }          });            module.exports = MyBase.extend({            exec: function () {              this.helper();            }          });</code></pre>    <h3>3.运行顺序</h3>    <p>运行顺序</p>    <p>Yeoman是按照优先级顺序依次执行所定义的方法。当你定义的函数名字是Yeoman定义的优先级函数名时,会自动将该函数列入到所在优先级队列中,否则就会列入到 default 优先层级队列中。</p>    <p>基本上执行的顺序如下: initializing</p>    <p>prompting</p>    <p>configuring</p>    <p>default</p>    <p>writing</p>    <p>conflicts</p>    <p>install</p>    <p>end</p>    <h3>4.generator Arguments</h3>    <p>Arguments是在命令行中直接传递的。 如:yo webapp my-project,接受键值对的条件。</p>    <p>desc:描述argument</p>    <p>required:定义是否必须</p>    <p>optional:是否可选择的</p>    <p>type:参数类型,支持的类型有String Number Array Object</p>    <p>defaults: argument默认值</p>    <p>banner:字符串显示的使用说明(这是默认提供)</p>    <p>注意:参数必须的定义在construct函数内,否则当你使用generator调用命令(如:yo webapp –help)的时候,不能够输出相关的帮助信息。</p>    <p>示例:</p>    <pre>  <code class="language-javascript">    var _ = require('lodash');          module.exports = generators.Base.extend({          // note: arguments and options should be defined in the constructor.          constructor: function () {            generators.Base.apply(this, arguments);              // This makes `appname` a required argument.            this.argument('appname', { type: String, required: true });            // And you can then access it later on this way; e.g. CamelCased            this.appname = _.camelCase(this.appname);          }        });</code></pre>    <h3>5. Options</h3>    <p>option和argument很相似,但是option是作为命令行标识使用的,如yo webapp –coffee。</p>    <p>我们可可以通过generator.option()添加option。</p>    <p>示例:</p>    <pre>  <code class="language-javascript">module.exports = generators.Base.extend({       // note: arguments and options should be defined in the constructor.       constructor: function () {         generators.Base.apply(this, arguments);           // This method adds support for a `--coffee` flag         this.option('coffee');         // And you can then access it later on this way; e.g.         this.scriptSuffix = (this.options.coffee ? ".coffee": ".js");       }    });</code></pre>    <h3>6. 输出消息</h3>    <p>输出消息是通过generator.log模块来处理实现的。不建议使用console.log输出命令。</p>    <p>示例:</p>    <pre>  <code class="language-javascript">    module.exports = generators.Base.extend({          myAction: function () {            this.log('Something has gone wrong!');          }        });</code></pre>    <h3>7. 处理依赖关系</h3>    <p>一般当你运行你的generator的时候,你经常需要通过 <a href="/misc/goto?guid=4958867133348946608" rel="nofollow">npm</a> 和 <a href="/misc/goto?guid=4958827487829750237" rel="nofollow">Bower</a>来安装一些generator用到的依赖模块。而这些任务是非常繁琐的,为了方便,yeoman将这部分任务抽离了出来。</p>    <p>npm</p>    <p>你只需要调用generator.npmInstall() 命令就可以执行npm安装命令,yeoman确保了npm install只执行了一次,即使他被多个generator调用。</p>    <p>例如你想安装lodash作为dev dependency:</p>    <pre>  <code class="language-javascript">   generators.Base.extend({         installingLodash: function() {           this.npmInstall(['lodash'], { 'saveDev': true });         }       });</code></pre>    <p>上面代码等同于调用了npm install lodash –save-dev命令。</p>    <p>Bower</p>    <p>你只需要调用generator.bowerInstall()即可启动安装命令。yeoman确保了bower install只执行了一次,即使他被多个generator调用。</p>    <p>npm && Bower</p>    <p>调用enerator.installDependencies()即可同时运行npm 和 bower。</p>    <p>其他tools</p>    <p>yeoman抽离了spawn命令,这个抽离保证了我们可以在Linux ,mac 以及windows系统上可以很好的运行。</p>    <p>假如你是一个PHP狂热爱好者,你想运行composer命令,你可以这样做:</p>    <pre>  <code class="language-javascript">    generators.Base.extend({          install: function () {            this.spawnCommand('composer', ['install']);          }        });</code></pre>    <p>请确保面spawn命令在install队列里。因为您的用户不愿意等待在那儿直到安装命令完成。</p>    <h3>8. 上下文路径</h3>    <p>为了方便文件流的输入输出,Yeoman使用两种位置环境。</p>    <p>1. 目标上下文</p>    <p>目标上下文定义为当前工作目录或含.yo-rc.json文件最接近的父文件夹。该.yo-rc.json文件定义了一个generator项目的根目录。该文件允许用户在子目录中运行命令,并让他们在项目中可以运行。这确保了用户行为的一致。</p>    <p>你可以通过generator.destinationRoot()命令获取目标路径,也可以通过generator.destinationPath(‘sub/path’)来拼一个路径:</p>    <pre>  <code class="language-javascript">    // Given destination root is ~/projects        generators.Base.extend({          paths: function () {            this.destinationRoot();            // returns '~/projects'              this.destinationPath('index.js');            // returns '~/projects/index.js'          }        });</code></pre>    <p>2. 模板上下文</p>    <p>模板上下文是你保存模板文件的目录,他一般是你要读取和复制的目录。模板上下文一般是默认是定义在./templates/目录的.你可以通过 generator.sourceRoot(‘new/template/path’)命令来重写。你可以通过 generator.sourceRoot()或者generator.templatePath(‘app/index.js’).来获取路径。</p>    <pre>  <code class="language-javascript">    generators.Base.extend({          paths: function () {            this.sourceRoot();            // returns './templates'              this.templatePath('index.js');            // returns './templates/index.js'          }    });</code></pre>    <p>3. “内存”文件系统</p>    <p>当涉及到覆盖用户的文件的时候,yeoman非常的谨慎,基本上,每一个write动作都是一个为已经存在的文件解决冲突的过程。帮助用户严重需要覆盖的内容。</p>    <p>4. 文件工具</p>    <p>generator的this.fs暴露出所有的文件方法,通过<a href="/misc/goto?guid=4959664199230894360" rel="nofollow">mem-fs editor </a>.</p>    <p>其他相关介绍请看<a href="/misc/goto?guid=4958524933186115738" rel="nofollow">官网</a></p>    <h3>9.完整示例</h3>    <p> </p>    <pre>  <code class="language-javascript">   'use strict';          var generators = require('yeoman-generator');        var mkdirp = require('mkdirp');        var yosay = require('yosay');        var chalk = require('chalk');        module.exports = generators.Base.extend({              constructor: function() {                generators.Base.apply(this, arguments);                this.option('coffee');                this.scriptSuffix = (this.options.coffee ? ".coffee": ".js");            },              initializing: function() {                var message = chalk.bgBlack.bold('\nWelcome to webApp\n') + chalk.underline('webApp.github.io\n');                this.log(yosay(message));            },              prompting: function() {                 var prompts = [{                    type:'input',                    name: 'appName',                    message: 'input app name .',                    default: 'webApp'                }];                this.prompt(prompts, function (answers) {                    this.log(answers);                }.bind(this));            },              configuring: function() {                this.config.save();            },              selfFunction: function () {                this.log("执行了自定义方法");            },              writing: function() {               this.fs.copyTpl(                 this.templatePath('index.html'),                 this.destinationPath('public/index.html'),                 { title: 'Templating with Yeoman' }               );            },          });</code></pre>    <p>——————————————–END——————————————–</p>    <p><em><strong>原文链接:<a class="external-link" href="/misc/goto?guid=4959664199845550928" rel="nofollow">http://yeoman.io/authoring/</a></strong></em></p>    <p><em><strong>翻译自 MaxLeap团队_UX成员:Jerry Zhang</strong></em></p>    <p><em><strong>来自:</strong></em>https://blog.maxleap.cn/archives/951</p>