簡體   English   中英

讓closure-compiler和Node.js發揮得很好

[英]Getting closure-compiler and Node.js to play nice

是否有任何項目一起使用node.js和closure-compiler(簡稱CC)?

官方的CC建議是一起編譯應用程序的所有代碼,但是當我編譯一些包含require("./MyLib.js")簡單node.js代碼時,該行直接放入輸出中,但它沒有在這種背景下沒有任何意義。

我看到幾個選項:

  1. 將整個應用程序編碼為單個文件。 這通過避免它來解決問題,但是對於維護是不利的。
  2. 假設在執行之前將連接所有文件。 這又避免了這個問題,但卻使實現未編譯的調試模式變得更加困難。
  3. 我想讓CC“理解”node.js require()函數,但如果不編輯編譯器本身就可能無法完成,可以嗎?

我一直在使用Closure Compiler with Node來完成我尚未發布的項目。 它采用了一些工具,但它有助於捕獲許多錯誤並且具有非常短的編輯 - 重啟 - 測試周期。

首先,我使用plovr (這是我創建和維護的項目),以便一起使用Closure Compiler,Library和Templates。 我以Closure Library的樣式編寫Node代碼,因此每個文件都定義了自己的類或實用程序集合(如goog.array )。

下一步是為要使用的Node函數創建一組externs文件。 我公開發表了一些這些內容:

https://github.com/bolinfest/node-google-closure-latitude-experiment/tree/master/externs/node/v0.4.8

雖然最終,我認為這應該是一個更加社區驅動的東西,因為有很多功能需要記錄。 (這也很煩人,因為一些Node函數有可選的中間參數而不是最后一個參數,使得類型注釋變得復雜。)我自己沒有開始這個動作,因為我們可以用Closure Complier做一些工作來減少它的尷尬(見下文)。

假設您已為Node命名空間http創建了externs文件。 在我的系統中,我已經決定,只要我需要http ,我將通過以下方式包含它:

var http = require('http');

雖然我沒有在我的代碼中包含require()調用。 相反,我使用Closure Compiler的output-wrapper功能在文件的開頭添加所有require() ,在plovr中聲明,在我當前的項目中如下所示:

"output-wrapper": [
  // Because the server code depends on goog.net.Cookies, which references the
  // global variable "document" when instantiating goog.net.cookies, we must
  // supply a dummy global object for document.
  "var document = {};\n",

  "var bee = require('beeline');\n",
  "var crypto = require('crypto');\n",
  "var fs = require('fs');\n",
  "var http = require('http');\n",
  "var https = require('https');\n",
  "var mongodb = require('mongodb');\n",
  "var nodePath = require('path');\n",
  "var nodeUrl = require('url');\n",
  "var querystring = require('querystring');\n",
  "var SocketIo = require('socket.io');\n",
  "%output%"
],

這樣,我的庫代碼從不調用Node的require() ,但編譯器容忍在我的代碼中使用http這樣的東西,因為編譯器將它們識別為externs。 由於它們不是真正的外部因素,因此必須按照我的描述進行。

最后,在討論列表中討論了這個之后,我認為更好的解決方案是為命名空間創建一個類型的新類型注釋:

goog.scope(function() {

    /** @type {~NodeHttpNamesapce} */
    var http = require('http');

    // Use http throughout.

});

在這種情況下,externs文件將定義NodeHttpNamespace ,以便Closure Compiler能夠使用externs文件對其進行類型檢查。 這里的不同之處在於,無論您想要什么,都可以將require()的返回值命名為,因為http的類型將是這種特殊的命名空間類型。 (為$確定一個“jQuery名稱空間”是一個類似的問題。)這種方法將不再需要為Node命名空間命名本地變量,並且不需要在plovr配置中使用那個巨大的output-wrapper

但這是一個題外話......一旦我按照上面所述設置了東西,我就有了一個shell腳本:

  1. 使用plovr在RAW模式下構建所有內容。
  2. 在plovr生成的文件上運行node

使用RAW模式會導致所有文件的大量連接(盡管它還負責將Soy模板甚至CoffeeScript轉換為JavaScript)。 不可否認,這使得調試變得很痛苦,因為行號是無稽之談,但到目前為止我一直運作良好。 Closure Compiler執行的所有檢查都使它值得。

閉包編譯器的svn HEAD似乎支持AMD

我用一種更簡單的方法取代了我的舊方法:

新的方法

  • 沒有require()調用我自己的應用程序代碼,僅用於Node模塊
  • 在運行或編譯之前,我需要將服務器代碼連接到單個文件
  • 使用簡單的grunt腳本完成連接和編譯

有趣的是,我甚至不必為require()調用添加extern。 Google Closure編譯器自動理解這一點。 我確實必須為我使用的nodejs模塊添加externs。

老方法

根據OP的要求,我將詳細闡述使用Google Closure Compiler編譯node.js代碼的方法。

我受到了bolinfest解決問題的方式的啟發,我的解決方案使用了同樣的原則。 不同之處在於我創建了一個執行所有操作的node.js腳本,包括內聯模塊(bolinfest的解決方案讓GCC負責處理)。 這使它更加自動化,但也更脆弱。

我剛剛為編譯服務器代碼的每一步添加了代碼注釋。 看到這個提交: https//github.com/blaise-io/xssnake/commit/da52219567b3941f13b8d94e36f743b0cbef44a3

總結一下:

  1. 我從我的主模塊開始,當我想運行它時,我傳遞給Node的JS文件。
    在我的例子中,這個文件是 start.js
  2. 在此文件中,使用正則表達式,我檢測所有 require()調用,包括賦值部分。
    在start.js中,這匹配一個require調用: var Server = require('./lib/server.js');
  3. 我根據文件名檢索文件所在的路徑,將其內容作為字符串獲取,並刪除內容中的module.exports賦值。
  4. 然后我將步驟2中的require調用替換為步驟3中的內容。除非它是核心node.js模塊,然后我將其添加到我稍后保存的核心模塊列表中。
  5. 第3步可能包含更多的 require()調用,所以我遞歸地重復步驟3和4,直到所有的 require()調用都消失了,我留下了一個包含所有代碼的巨大字符串。
  6. 如果所有遞歸都已完成,我使用REST API編譯代碼。
    您也可以使用脫機編譯器。
    我有每個核心node.js模塊的externs。 此工具可用於生成外部
  7. 我預先發布了已刪除的core.js模塊, require調用已編譯的代碼。

預編譯代碼。
所有 require呼叫都被刪除。 我的所有代碼都被夷為平地。
http://pastebin.com/eC2rVMiN

編譯后的代碼。
Node.js核心 require手動預先調用。
http://pastebin.com/uB8CaejN


為什么你不應該這樣做:

  1. 它使用正則表達式(不是解析器或標記器)來檢測 require調用,內聯和刪除 module.exports 這很脆弱,因為它不包括所有語法變體。
  2. 內聯時,所有模塊代碼都會添加到全局命名空間中。 這違反了Node.js的原則,其中每個文件都有自己的命名空間,如果您有兩個具有相同全局變量的不同模塊,這將導致錯誤。
  3. 它不會提高代碼的速度,因為V8還執行了大量的代碼優化,如內聯和死代碼刪除。

為什么你應該:

  1. 因為它在您具有一致的代碼時確實有效。
  2. 啟用詳細警告時,它將檢測服務器代碼中的錯誤。

Node.js上的Closure Library在60秒內完成。

它受支持,請訪問https://code.google.com/p/closure-library/wiki/NodeJS

選項4:不要使用閉包編譯器。

節點社區中的人不傾向於使用它。 你不需要縮小node.js源代碼,這很愚蠢。

對縮小沒有好處。

至於關閉的性能優勢,我個人懷疑它實際上使您的程序更快。

當然還有成本,調試編譯的JavaScript是一場噩夢

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM