使用YEOMAN創建屬于自己的前端工作流

a158318 8年前發布 | 42K 次閱讀 工作流 HTML JavaScript開發 gulp

背景

前幾天看了一篇文章大受啟發:我理想中的前端工作流,現在工作中一直在使用gulp和webpack做自動化,在單獨項目中效果很不錯。但是隨著項目逐漸怎多,是需要使用一個工具來幫你快速創建和規范項目。

Yeoman是什么

Yeoman是一個腳手架,可以快速生成一個項目的骨架。官網上有很多大家 已經寫好的腳手架 ,也可以自己寫一個適合自己的,接下來我會翻譯下官網的教程,學習的同時把經驗分享給大家。

Yeoman能做什么

Yeoman只是幫我們生成項目的骨架是遠遠不夠的,官網上介紹,Yeoman是由三部分組成的:腳手架工具 - Yo、構建工具 - Grunt or Gulp、包管理工具 - Bower or npm。

創建Yeoman

翻譯yeoman官網Creating a generator流程

(一)Getting started 快速開始

(二)Running Context 生命周期

(三)User Interactions 和使用者互動

(四)composability 組合

(五)Managing Dependencies 依賴管理

(六)Interacting with the file system 文件操作

(一)快速開始

原文地址:http://yeoman.io/authorin...

1.1 建立一個node模塊

1.首先要建立一個文件夾,在這個文件夾內寫你的generator,這個文件夾的名字 必須 被命名為 generator-name ,name為你generator的名字,假如我想寫一個vue的腳手架,我可以命名為: generator-vue ,這個很關鍵,Yeoman文件系統只會信任這種規范的generator。

mkdir generator-vue

2.建立node模塊,首先要必備文件 package.json ,這個文件可以通過執行 npm init 指令來生成,前提是需要安裝 node 及 npm。

{
  "name": "generator-vue",
  "version": "0.1.0",
  "description": "",
  "files": [
    "app",
    "router"
  ],
  "keywords": ["yeoman-generator"],
  "dependencies": {
    "yeoman-generator": "^0.20.2"
  }
}

幾點要求:

  1. name:必須格式為 generator-你項目的名字 。

  2. keywords:數組中必須有 yeoman-generator ,這樣你的項目才會被 Yeoman官方的generators列表 所收錄。

  3. 如果需要添加其他的屬性可以去 npm官網文檔 中查看。

1.2 文檔結構

通過1.1步驟已經有了package.json,下一步在新建兩個文件夾分別叫app和router,結構如下。

├───package.json
├───app/
│   └───index.js
└───router/
    └───index.js

1.2.1 默認項目

當你執行Yeoman指令 yo vue (上面你已經建立的項目名字)的時候,他會默認執行你根目錄下app/index.js的內容,所以一個新項目, app/ 目錄是必須的。

1.2.2 子項目

router/ 作為子項目,可以通過 yo vue:router 來執行。

1.2.3 更改文件目錄

├───package.json
└───generators/
    ├───app/
    │   └───index.js
    └───router/
        └───index.js

如果不喜歡把所有項目都放在根目錄下,Yeoman還允許把項目放在 generators/ 下面,改寫上面的例子:

├───package.json
└───generators/
    ├───app/
    │   └───index.js
    └───router/
        └───index.js

如果更改了文件目錄,要同步修改 package.json 中對應文件的目錄結構:

{
  "files": [
    "generators/app",
    "generators/router"
  ]
}

1.3 擴展generator

1.3.1 重寫構造函數

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>

1.3.2 添加自己的方法

module.exports = generators.Base.extend({
  method1: function () {
    console.log('method 1 just ran');
  },
  method2: function () {
    console.log('method 2 just ran');
  }
});

下一步的時候當你運行generator的時候,會看到這兩句console輸出在控制臺。

1.4 運行generator

1.4.1 安裝全局generator

在項目跟路徑下 generator-name(vue)/ 執行指令:

npm link

過程中,將會安裝node模塊依賴,和創建軟連接指向你當前項目。

//1.到本地全局node模塊路徑下
cd /usr/local/lib/node_modules

//2.查看列表 ll

//3.會看到已經安裝了一個全局的geneator-vue模塊 npm geneator-vue -> /Users/lvjinlong/generator-vue gulp ..

//4.此時在任意新建的項目文件夾中yo項目的名字,會看到上面實例中的console打印出來的結果。 yo vue</code></pre>

1.4.2 尋找根目錄

安裝geneator的時候,Yeoman會搜索你的文件夾,會把包含 .yo-rc.json 文件的文件夾作為你的根目錄來初始化項目。

問:那么, .yo-rc.json 是個什么東西呢?

答:當你 第一次 調用 this.config.save() 的時候,系統會生成這個文件。

問:那么, this.config.save() 這個方法的作用是什么呢?

答: 官網 這篇文章會有講解,大體意思是會利用 .yo-rc.json 來存儲或是讀取用戶相關信息。

所以當你初始化一個項目的時候,別忘記清除掉之前系統生成的 .yo-rc.json 。

(二)生命周期

原文地址:http://yeoman.io/authorin...

2.1 Prototype methods as actions

每個方法會直接附加在generator原型上作為一個action,每個action按照一定的循序執行在Yeoman的生命周期中。

這些方法相當于直接執行了 Object.getPrototypeOf(generator) ,

所有方法都會 自動執行 。如果不想都自動執行,請往下看。

2.1.1 私有方法

只有私有方法在Yeoman中才不會自動執行,下面有三種辦法幫你創建一個私有方法。

1. 在方法名前面加下劃線(例如: _method )

2. 使用實例方法

generators.Base.extend({
  init: function () {
    this.helperMethod = function () {
      console.log('won\'t be called automatically');
    };
  };
});

3.繼承父generator

var MyBase = generators.Base.extend({
    helper: function () {
      console.log('won\'t be called automatically');
    }
  });

module.exports = MyBase.extend({ exec: function () { this.helper(); } });</code></pre>

2.2 生命周期

Yeoman中的定義了生命周期鉤子,和這些鉤子命名相同的會按照順序執行,如果和這些鉤子名稱不一樣則默認為 default

這些生命周期鉤子 按順 序為:

  1. initializing:初始化方法(檢驗當前項目狀態、獲取configs、等)

  2. prompting:獲取用戶選項

  3. configuring:保存配置(創建 .editorconfig 文件)

  4. default:如果函數名稱如生命周期鉤子不一樣,則會被放進這個組

  5. writing:寫generator特殊的文件(路由、控制器、等)

  6. conflicts:沖突后處理辦法

  7. install:正在安裝(npm、bower)

  8. end:安裝結束、清除文件、設置good bye文案、等

(三)和用戶互動

原文地址:http://yeoman.io/authorin...

Yeoman默認在終端中執行,但是也支持在多種不同工具中執行。這時候我們使用 console.log() 或是 process.stdout.write() 用戶就可能看不到,Yeoman中使用 generator.log() 來統一打印輸出結果。

3.1和用戶互動

3.1.1 Prompts - 提示框

提示框是Yeoman主要和用戶交流的手段,是通過 Inquirer 模塊來實現的,所有的API及參數可以看 這里 ,執行以下實例看下效果:

module.exports = generators.Base.extend({
  prompting: function () {
    var done = this.async();
    this.prompt({
      type    : 'input',
      name    : 'name',
      message : 'Your project name',
      default : this.appname // Default to current folder name
    }, function (answers) {
      this.log(answers.name);
      done();
    }.bind(this));
  }
})

3.1.2 Remembering user preferences 記錄用戶預設參數

一個確定的答案,比如帳號,用戶可能多次提交同一個答案,這時候可以用Yeoman提供 store 來存儲這些答案。

this.prompt({
  type    : 'input',
  name    : 'username',
  message : 'What\'s your Github username',
  store   : true
}, callback);

這時候會在跟路徑下生成一個 .yo-rc.json 文件,里面會存儲name信息。 可以參考官網storage這一節

3.1.3 Arguments - 參數

參數直接通過命令行傳遞,例如:

yo webapp my-project

這個例子中,my-project 是第一個參數。

通知系統我們需要參數,我們使用 generator.argument() 方法,這個方法接受兩種形式:

  1. name(String) -- generator['name']

  2. hash(key-value) -- 哈希值的形式,接受以下參數作為key值

    • desc -> 參數描述

    • required -> 是否為必須傳遞 [ ture | false ]

    • optional -> 是否可選 [ ture | false ]

    • type -> 參數類型 [ String | Number | Array | Object]

var _ = require('lodash'); //需要提前安裝lodash模塊,提供一些常用方法
module.exports = generators.Base.extend({
  //注: arguments和options必須在constructor中定義.
  constructor: function () {
    generators.Base.apply(this, arguments);
    //appname為一個必須的參數
    this.argument('appname', { type: String, required: true });
    //用駝峰式把這個參數保存起來
    this.appname = _.camelCase(this.appname);
  }
});

3.1.4 Options - 選項

Options(選項)看起來像是Arguments(參數),但是他們是在命令行中的標志。

實例:舉一個官網團隊的腳手架demo - webapp - 15行

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);
    this.option('skip-welcome-message', {
      desc: 'Skips the welcome message',
      type: Boolean
    });
  }
})

用法:

webapp - options

yo webapp --skip-install

3.2 輸出信息

輸出信息使用 generator.log 模塊,和js的 console.log() 基本一致。

module.exports = generators.Base.extend({
  myAction: function () {
    this.log('Something has gone wrong!');
  }
});

傳值的方式同Arguments(參數),字符串或hash。區別是參數:

  • desc:描述

  • alias:簡寫(--version 簡寫為 -v)

  • type:[ Boolean | String | Number ]

  • defaults:默認值

  • hide :[ Boolean ] 是否隱藏幫助信息

(四)組合

原文地址:http://yeoman.io/authorin...

很有趣的是,官網的第一個demo竟然是一個變形金剛組合的 gif ,可見他們是多么想表達各個小功能組合起來后的yeoman是有多強大。

可以通過以下兩種方式開始組合:

  1. 依賴另外一個generator(例如: generator-backbone 使用 generator-mocha )。

  2. 使用者,根據自己的需求在初始化項目的時候選擇配置。(例如: sass 或者 less 來搭配 webpack 或是 gulp )

4.1 generator.composeWith()

composeWith 方法允許你的generator來組合別人的generator,但是一旦組合成功,不要忘記第二章的內容 <(二)Running Context 生命周期> ,所有被組合的generator都遵循Yeoman的生命周期規則來順序執行,不同的generator執行順序,取決于 composeWith 調用他們的順序,看下面的API及執行順序實例。

4.1.1 API

composeWith 接收三個參數:

  1. namespace :聲明generator和誰組合。[ String ]

  2. options :調用generator的時候需要接收的參數。[ Object | Array ]

  3. settings :你的generator用這些配置來決定如果運行其他的generators。[ Object ]

    • settings.local :需要在 dependencies 中配置,使用dependencies安裝的模塊相當于本地模塊,這里使用 require.resolve 來返回一個本地模塊的路徑,如: node_modules/generator-name [ String ]

    • settings.link : weak or strong [ String ]

      • week link:在初始化的時候不運行,比如后端運行的,frameworks或css的預處理。

      • strong link:一直運行。

當需要用 peerDependencies 來組合generator

this.composeWith('backbone:route', { options: {
  rjs: true
}});

當需要用 dependencies 來組合generator

this.composeWith('backbone:route', {}, {
  local: require.resolve('generator-bootstrap')
});

//注:require.resolve()將返回node.js需要的模塊路徑。</code></pre>

接下來4.2中會解釋 peerDependencies 和 dependencies 的區別。

4.1.2 執行順序實例

// In my-generator/generators/turbo/index.js
module.exports = require('yeoman-generator').Base.extend({
  'prompting' : function () {
    console.log('prompting - turbo');
  },

'writing' : function () { console.log('writing - turbo'); } });

// In my-generator/generators/electric/index.js module.exports = require('yeoman-generator').Base.extend({ 'prompting' : function () { console.log('prompting - zap'); },

'writing' : function () { console.log('writing - zap'); } });

// In my-generator/generators/app/index.js module.exports = require('yeoman-generator').Base.extend({ 'initializing' : function () { this.composeWith('my-generator:turbo'); this.composeWith('my-generator:electric'); } });</code></pre>

來分析下上面這段腳本:

  1. 以上這段腳本在初始化的時候執行了兩個 composeWith 方法 turbo 和 electric。

  2. 分別執行了他們目錄下的index.js。

  3. 加載順序判斷:turbo 優先于 electric。

  4. 生命周期問題:prompting 優先于 writing。

所以執行后的結果如下:

prompting - turbo
prompting - zap
writing - turbo
writing - zap

4.2 peerDependencies 和 dependencies 的區別

npm允許以下三種dependencies(依賴):

  1. dependencies :使用依賴,自己或是別人使用你的generator所必備的依賴模塊。這些模塊被generator視為本地模塊。

  2. peerDependencies :看下面的 注: npm@3后,peerDependencies不會再被自動安裝,需要手動。

  3. devDependencies :開發依賴,作為開發或者是測試需要用的模塊,如果別人安裝你的generator,這些模塊不應該被安裝。

當使用 peerDependencies 別的模塊也要依賴當前這個模塊,小心不要創建版本導致沖突,Yeoman推薦使用(>=) 或 (*) 來安裝可用的版本,如:

{
  "peerDependencies": {
    "generator-gruntfile": "*",
    "generator-bootstrap": ">=1.0.0"
  }
}

注:npm@3以后, peerDependencies 不會再被自動安裝,安裝他們必須執行如下:

npm install generator-yourgenerator generator-gruntfile generator-bootstrap@">=1.0.0"

(五)依賴管理

原文地址:http://yeoman.io/authorin...

Yeoman提供以下幾種形式來安裝依賴。

5.1 npm

使用 generator.npmInstall() 來安裝npm包,如果你在多個generators調用了 npm install Yeoman保證只會執行一次。

例如:你需要安裝 lodash 這個模塊作為發開依賴。

generators.Base.extend({
  installingLodash: function() {
    this.npmInstall(['lodash'], { 'saveDev': true });
  }
});

效果等同于直接在終端輸入:

npm install lodash --save-dev

5.2 Bower

使用 generator.bowerInstall() 來安裝依賴。實例:同npm。

5.3 Both npm & Bower

使用 generator.installDependencies() 來同時安裝npm 和 bower。實例:同npm。

5.4 Using other tools

可以使用 spawnCommand 來安裝其他工具。比如:PHP的composer。

(六)文件操作

原文地址:http://yeoman.io/authorin...

6.1 根路徑

Yeoman會在這個根路徑中創建你項目的腳手架。

根路徑會以以下兩種方式定義:

  1. 當前工作路徑

  2. 最近一級中包含 .yo-rc.json 的路徑

你可以通過Yeoman提供的 generator.destinationRoot() 方法來獲取根路徑,這個方法接收一個參數 generator.destinationPath('sub/path') 來獲取子目錄的路徑。

例如:

查看當前路徑

$ pwd
~/projects
//跟路徑是 ~/projects
generators.Base.extend({
  paths: function () {
    this.destinationRoot();
    // returns '~/projects'

this.destinationPath('/sub/index.js');
// returns '~/projects/sub/index.js'

} });</code></pre>

6.2 常用工作路徑

原文是Template context,其實我感覺直譯不太好,換做叫常用工作路徑會更好。

這個路徑的默認取你當前目錄 ./templates/ , 可以手動覆蓋這個路徑 generator.sourceRoot('new/template/path')

例如:

generators.Base.extend({
  paths: function () {
    this.sourceRoot(); //設置常用工作路徑
    // returns './templates'

this.templatePath('index.js'); //讀取常用工作路徑
// returns './templates/index.js'

} });</code></pre>

6.3 文件操作

所有文件相關的方法都會通過 this.fs 暴露出來。 這里有所有文件操作相關方法 ,包括下面的 copyTpl 方法。

實例: 把一個 常用工作路徑 的文件復制到 根路徑 下,并傳一個參數。

1.常用工作路徑下的 ./templates/index.html 內容是:

<html>
  <head>
    <title><%= title %></title>
  </head>
</html>

2.我們用 copyTpl 方法把來復制文件,該方法使用 ejs模板語法

generators.Base.extend({
  writing: function () {
    this.fs.copyTpl(
      this.templatePath('index.html'),//第一個參數:from
      this.destinationPath('public/index.html'),//第二個參數:to
      { title: 'Templating with Yeoman' }//第三個參數:options
    );
  }
});

3.來看復制后的 public/index.html

<html>
  <head>
    <title>Templating with Yeoman</title>
  </head>
</html>

6.4 通過「流」來改變文件

Yeoman提供 registerTransformStream() 方法,使用gulp的來操作文件。

例如:

var beautify = require('gulp-beautify');
this.registerTransformStream(beautify({indentSize: 2 }));

6.5 修改已經存在文件的內容

Yeoman介紹了幾個比較流行的解析器:

1. Cheerio for parsing HTML,基本實現流程如下

var cheerio = require('cheerio'),
    $ = cheerio.load('<h2 class="title">Hello world</h2>');

$('h2.title').text('Hello there!'); $('h2').addClass('welcome');

$.html(); //=> <h2 class="title welcome">Hello there!</h2></code></pre>

2. Esprima for parsing JavaScript

3. For JSON files 可以使用JSON原生的方法

本次只翻譯了前六章,后續會翻譯后六章、自己如果寫一個generator以及遇到的坑和問題。都會更新在我的github的Yeoman-article中。

github: https://github.com/tonyljl526/yeoman

來自: https://segmentfault.com/a/1190000004896264

 本文由用戶 a158318 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!