2016 年 11 月

第 31 卷,第 11 期

本文章是由機器翻譯。

程式設計師雜談 - 如何使用 MEAN: 採取 Gulp

Ted Neward | 2016 年 11 月

Ted Neward歡迎回來,MEANers。

如果您閱讀我的上一篇專欄中提到的 「 重新開機 」 的程式碼基底 10 月 (若要建立的基礎結構使用 Yeoman 和黏附程式碼),您可能已經注意到新的工具已擲回不說明混合。我把,當然,用來啟動伺服器,並開啟用戶端瀏覽器,以建構用戶端應用程式的 「 大 」 工具 (msdn.com/magazine/mt742874)。

如果您錯過了我的上一篇專欄,很容易趕上。首先,請確定 Yeoman,兩者都安裝 Node.js 開發環境 (連同 MongoDB 的本機執行複本) 上的 「 角度 fullstack 」 產生器︰

npm install –g yeoman angular-fullstack-generator
yo angular-fullstack

然後回答問題,到底有哪些工具 Yeoman 應該建立位置的結構之後 (如本專欄中,所做的選擇大多不相關),和 Yeoman 好心已經開始之後關閉 npm 的 「 安裝 」 以提取所有執行階段和開發相依性,scaffolder 會報告已準備好要執行 「 gulp 測試 」 或 「 gulp 開始︰ 伺服器 」,藉以測試應用程式

很明顯地,Gulp 是任何內容,它是某種 akin 精神在樣式的建置工具 MSBuild 或 meister但它的運作方式是有點不同的三種工具,必須加以討論結果。

只是小大啟動

雖然不完全呼叫 Gulp < 建置工具 > 以往不會建置的語言 (請記住,ECMAScript 通常就是要解譯的語言),就是這麼簡單的一種工具,要先確認所有項目對齊 (期間或之後) 執行開發並準備使用的最佳術語。或許更好的名稱,它們會是 「 開發自動化工具 」,因為是更精確的並且會包含編譯程式碼,並組合成可部署的成品的動作。不過,因為說來話長,而只是 「 建置工具 」 會關閉語言,我們來使用 Gulp 是建置工具,現在的概念。

若要開始使用 Gulp,讓我們先前中斷的建構程式碼離開並從頭開始將重點放在大項目 (和方法) 沒有作用。安裝的全域 Gulp 命令列工具 (「 npm 安裝-g gulp-cli 」) 第一次。然後,如果在新的目錄中,您可以建立空白的 Node.js 專案執行下列命令︰

npm init
npm install --save-dev gulp

如此可確保 Gulp 參考 package.json 檔案中,為開發人員的相依性,因此,如果您下拉專案就不需要記得安裝 Gulp — 它只是稍後會與下一步 npm 「 安裝 」 的全新環境大功告成了。

現在,在所選的文字編輯器中,建立一個新的檔案,稱為 gulpfile.js:

const gulp = require('gulp');
gulp.task('default', function() {
  console.log("Gulp is running!");
});

然後,從相同的目錄發出股票 Gulp 命令 (可想而知) 是只是 「 大 」。 Gulp 秒,思考,然後傳回︰

[18:09:38] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[18:09:38] Starting 'default'...
Gulp is running!
[18:09:38] Finished 'default' after 142 μs

還不賴。目前,但還不賴,看起來有點大材小用。

Gulp 下一些工作

如同大部分建置工具,根據 「 工作 」,以及更重要的是,會認為 gulp 如何追蹤這些工作之間的相依性。因此,您也可以在這裡簡單 Gulpfile,它會看到已有一項工作,稱為 「 預設 」 (這是大家熟悉的慣例來表示,如果未指定命令列上應該執行的工作),並執行相關聯的常值,當系統要求您執行該工作函式的主體。以不同的方式命名工作便微不足道︰

const gulp = require('gulp');
gulp.task('echo', function() {
  console.log("Gulp is running!");
});

它應該很明顯,Gulp 是只是程式碼中,因此可以透過程式碼的任何項目可以完成 Gulp 工作主體中。這提供了許多不可思議選項的詳細資訊,例如讀取資料庫以尋找要程式碼產生需要的項目、 設定參數,其他線上服務進行交談或甚至只列印目前的日期和時間︰

const gulp = require('gulp');
gulp.task('echo', function() {
  console.log("Gulp is running on " + (new Date()));
});

這會產生︰

Teds-MacBook-Pro:gulpdemo tedneward$ gulp echo
[18:16:24] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[18:16:24] Starting 'echo'...
Gulp is running on Wed Sep 14 2016 18:16:24 GMT-0700 (PDT)
[18:16:24] Finished 'echo' after 227 μs

相依的工作只被列在字串陣列之間的名稱以及工作其常值函式主體,以便讓工作 「 回應 」 根據另一個工作只是看起來像這樣︰

const gulp = require('gulp');
const child_process = require('child_process');
gulp.task('gen-date', function() {
  child_process.exec('sh date > curdate.txt');
});
gulp.task('echo', ['clean', 'gen-date'], function() {
  console.log("Gulp is running on " + (new Date()));
});

在這裡,「 gen 日期 」 工作會使用標準的 Node.js 封裝、 「 child_process,」 開始進行的外部工具的檔案,是為了要證明您可以寫入的日期。老實說,都很方便,但通常建置工具會執行一些更高的匯入不是只寫入主控台中的項目,並找出目前的日期和時間。

Gulping 有點多

讓我們將更多肉放入這。下列 ECMAScript 中的程式碼,包括不很好的程式碼組件建立 index.js 檔案︰

// index.js
function main(args) {
  for (let arg in args) {
    if (arg == "hello")
      console.log("world!");
      console.log("from index.js!");
    }
}
console.log("Hello, from index.js!")
main()

是,它是無意義的位元,[是],它顯然有幾個問題,但這重點,那就天下太平了一種工具,無法找出這些問題並報告它們一樣。(dare 我說,「 編譯時期檢查 」 嗎?) 所幸,這類工具存在於 JSHint (jshint.com),但根據預設,命令列工具安裝,而且會一直執行,請記得將提出。

所幸,這就是建置工具。讓我們回到 Gulpfile,開始執行每個原始程式檔 (其中都只有一個現在) 告訴它的相關問題的來源檔案,然後要求它執行每個 JSHint JSHint Gulp:

// Don't need it in this file, but we need it installed
require('jshint');
require('jshint-stylish');
const gulp = require('gulp');
const jshint = require('gulp-jshint');
gulp.task('jshint', function() {
  return gulp.src('*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

當您執行此動作時,此工具會指出有一些建議的變更,所有在目前的目錄中,包括本身 Gulpfile"js"檔案。Bah。您不想要的檢查 Gulpfile,讓我們畫面它要執行的檔案集合︰

gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

這樣會從目前的檔案清單中移除 「 gulpfile.js 」,再將其傳遞到管線中的下一個階段。事實上,您可能會想大部分的程式碼存在於"src"目錄 (或 「 伺服器 」 和 「 用戶端 」 的目錄,為每個邊),因此將那些和任何其子目錄加入至要處理的檔案清單︰

gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js', 'server/**/*.js', 'client/**/*.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

「 雙-星星 」 在每個路徑中取得的遞迴行為挑選每個這些子目錄中的任何"js"檔案。

在它的介面中,這很重要,但它功用不多: 您仍然必須 jshint 類型 「 大 」 (或只是 「 gulp 「 如果繫結到 「 預設 」 工作與相依性) 以手動方式每的次您想要查看需要修正。為什麼無法只需要執行的程式碼變更,Ide 執行方式的一件事?

當然,這樣做,請如所示 [圖 1

[圖 1 的連續自動化使用 Gulp

// Don't need it in this file, but you need it installed
require('jshint');
const gulp = require('gulp');
const jshint = require('gulp-jshint');
gulp.task('default', ['watch']);
gulp.task('watch', function() {
  gulp.watch(['*.js', '!gulpfile.js', 'client/**/*.js', 'server/**/*.js'],
    ['jshint']);
});
gulp.task('jshint', function() {
  return gulp.src(['*.js', '!gulpfile.js', 'client/**/*.js', 'server/**/*.js'])
    .pipe(jshint())
    .pipe(jshint.reporter('default'));
});

現在,當您執行 「 gulp 」,在命令列將會只是暫停並等候。在 「 監看 」 模式中,讓它將敬請期待的任何檔案傳遞至 「 gulp.watch 」 呼叫,以及那些如果有任何檔案的變更現在是 「 gulp (亦即,它們會儲存 — Gulp 無法對不幸的是等文字編輯器內),它會立即執行一組完整的檔案的 「 jshint 」 工作。並請持續留意。

更 gulping

若要了解如何 Gulp 工作處理檔案的索引鍵的其中一個埋管道的呼叫中。Gulp 認為根據 「 資料流 」 而不是工作或檔案。比方說,簡單的 Gulp 工作會從"src"目錄複製到 「 目的地 」 目錄看起來會像這樣︰

gulp.task('copy-files', function() {
  gulp.src('source/folder/**')
    .pipe( gulp.dest('dest/folder/**') );
});

在本質,檔案會每個由 gulp.src,挑選儲放,原貌,到 gulp.dest 所指定的目的地。需要這些檔案會發生此問題只會管線中的步驟,每個檔案會流經管線的下一個步驟之前,該管線。它是 Unix 架構樣式的 「 管道與篩選器 」 引進 Node.js 生態系統變得相當優雅的方式。Windows PowerShell 是根據相同的架構,對於他們已在 [.NET 宇宙中看到類似下面的之前的想法。

因此,比方說,如果您不想要 Gulp 觸控透過管線的每個檔案,沒有外掛程式,該 (「 gulp-filelogger 」),而且它會列印到主控台上的每個檔案它修飾,請參閱︰

gulp.task('copy-files', function () {
  gulp.src(srcFiles)
    .pipe(filelogger())
    .pipe(gulp.dest(destDir));
});

這會產生下列︰

Teds-MacBook-Pro:gulpdemo tedneward$ gulp copy-files
[20:14:01] Using gulpfile ~/Projects/code/gulpdemo/gulpfile.js
[20:14:01] Starting 'copy-files'...
[20:14:01] Finished 'copy-files' after 14 ms
[20:14:01] [/Users/tedneward/Projects/code/gulpdemo/index.js]
Teds-MacBook-Pro:gulpdemo tedneward$

請注意,輸出會顯示 Gulp 報表完成之後。Gulp 可以 (且沒有) 會處理這些資料流以非同步的方式有太多時間,縮短 [組建] 時間。大部分的情況下,開發人員都不知道也不小心項目平行執行,但如需這些執行精確的序列中的項目是重要的時間,不用多說,Gulp 外掛程式社群有某些外掛程式,將序列化執行,而且確定所有項目會在序列。Gulp 4.0 加入兩個新的函式、 平行和序列,以達成更清楚,但是因為它尚未尚未推出,您必須在等候。

順便一提,Gulp 本身組成純粹為止所看到的四個函數︰ gulp.task、 gulp.watch、 gulp.src 和 gulp.dest。其他項目是所有的外掛程式、 npm 模組,或以手動方式寫入。這樣可以讓 Gulp 本身非常容易了解。Depressingly 簡單,事實上的文章,作者付費的字。

一次 gulping 許多

Gulp 本身不是所有複雜的工具,但與這一類的任何工具,其真正的優勢在於各式各樣互補超出其周圍社群應有的工具和外掛程式。完整清單位於 gulpjs.com/plugins, ,但 [圖 2 Gulp 配方,示範如何將專案發行到 GitHub,包括 Git 命令將推送至主要自動化的代表性範例會顯示。

[圖 2 Gulp 配方

var gulp = require('gulp');
var runSequence = require('run-sequence');
var conventionalChangelog = require('gulp-conventional-changelog');
var conventionalGithubReleaser = require('conventional-github-releaser');
var bump = require('gulp-bump');
var gutil = require('gulp-util');
var git = require('gulp-git');
var fs = require('fs');
gulp.task('changelog', function () {
  return gulp.src('CHANGELOG.md', {
    buffer: false
  })
    .pipe(conventionalChangelog({
      preset: 'angular' // Or to any other commit message convention you use.
    }))
    .pipe(gulp.dest('./'));
});
gulp.task('github-release', function(done) {
  conventionalGithubReleaser({
    type: "oauth",
    token: '' // Change this to your own GitHub token.
  }, {
    preset: 'angular' // Or to any other commit message convention you use.
  }, done);
});
gulp.task('bump-version', function () {
// Hardcode the version change type to "patch," but it might be a good
// idea to use minimist (bit.ly/2cyPhfa) to determine with a command
// argument whether you're doing a "major," "minor" or a "patch" change.
  return gulp.src(['./bower.json', './package.json'])
    .pipe(bump({type: "patch"}).on('error', gutil.log))
    .pipe(gulp.dest('./'));
});
gulp.task('commit-changes', function () {
  return gulp.src('.')
    .pipe(git.add())
    .pipe(git.commit('[Prerelease] Bumped version number'));
});
gulp.task('push-changes', function (cb) {
  git.push('origin', 'master', cb);
});
gulp.task('create-new-tag', function (cb) {
  var version = getPackageJsonVersion();
  git.tag(version, 'Created Tag for version: ' + version, function (error) {
    if (error) {
      return cb(error);
    }
    git.push('origin', 'master', {args: '--tags'}, cb);
  });
  function getPackageJsonVersion () {
    // Parse the json file instead of using require because require caches
    // multiple calls so the version number won't be updated.
    return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
  };
});
gulp.task('release', function (callback) {
  runSequence(
    'bump-version',
    'changelog',
    'commit-changes',
    'push-changes',
    'create-new-tag',
    'github-release',
    function (error) {
      if (error) {
        console.log(error.message);
      } else {
        console.log('RELEASE FINISHED SUCCESSFULLY');
      }
      callback(error);
    });
});

這個範例會示範幾個事項︰ 如何執行特定的順序,來產生慣例改變使用 Gulp 外掛程式的工作設定檔案中,執行 GitHub 樣式版本的訊息,撞語意的版本和一大堆多個。所有從 gulp 版本;這是非常有用。

總結

這並未一個特別的程式碼為主的發行項,但您只是重新啟動整個應用程式獲得一大堆的功能,並且基本上處於相同層級 (和之外) 的應用程式從什麼您擁有一直在建立過去一年左右。你喜歡樣板 !

更重要的是,需要以手動方式執行樣板之前建置的所有組件拼湊各個部分很容易了解程式碼,以及整個來龍去脈位置。比方說,打開 routes.js 看起來很熟悉您建置以手動方式更早版本,路由表,package.json 檔案中 (在專案目錄的根目錄) 將會變大,但會保持相同,您已使用。

只新東西,事實上,除了 Yeoman 本身使用,會收集到正確的位置,所有相關的組件建置工具的簡介,這將是我探討下一次。在那之前...祝各位寫程式 !


Ted Neward 是西雅圖 polytechnology 顧問、 講師及指導。 他已寫入 100 個以上的文章,是 F #MVP、 具有作者及合著者著作。與他連絡 ted@tedneward.com 您想要讓他來自與您的小組,或是閱讀他的部落格 blogs.tedneward.com

由於閱本篇文章的下列技術專家︰ Shawn Wildermuth