![](/img/trans.png)
[英]How to use google-closure-compiler-js for a node.js app without gulp/grunt/webpack?
[英]How to use the Google Closure Compiler to browserify your Node.js library
我有這個簡單的 Node.js 庫:
mylib/
|- inc.js
|- index.js
|- is_number.js
|- package.json
mylib/is_number.js
module.exports = x => typeof x === 'number';
mylib/inc.js
const is_number = require('./is_number');
module.exports = x => is_number(x) ? x + 1 : x;
mylib/index.js
(我的package.json
中的main
屬性的值)
module.exports = {
inc: require('./inc'),
utils: {
is_number: require('./is_number')
}
};
例子:
const mylib = require('mylib');
mylib.inc(41);
//=> 42
mylib.utils.is_number(42);
//=> true
如何使用 Google Closure Compiler “瀏覽”我的 Node.js 庫,以便它也可以在瀏覽器中工作? 例如,
<script src="mylib/browser.min.js"></script>
<script>
const mylib = window.mylib;
mylib.inc(41);
//=> 42
mylib.utils.is_number(42);
//=> true
</script>
這個答案的規范帖子是這個Gist 。
創建mylib/index_browser.js
window.mylib = { inc: require('./inc'), utils: { is_number: require('./is_number') } };
創建mylib/externs.js
/** @externs */ var mylib; var inc; var utils; var is_number;
然后:
$ cc --compilation_level ADVANCED \ --language_out ES5 \ --process_common_js_modules \ --module_resolution NODE \ --externs mylib/externs.js \ --isolation_mode IIFE \ --js mylib/index_browser.js mylib/inc.js mylib/is_number.js \ --js_output_file mylib/browser.min.js
其中cc
是您的 Google Closure Compiler 實例的別名; 請參閱下面的示例
在我們開始之前:
我寫這個別名是為了更容易調用 Google Closure Compiler (CC)
$ alias cc="java -jar /devtools/closure-compiler/compiler.jar"
$ cc --version
Closure Compiler (http://github.com/google/closure-compiler)
Version: v20210106
該庫的瀏覽器版本將被編譯為 ES5。
您的第一次嘗試可能如下所示:只需編譯導出文件mylib/index.js
$ cc --compilation_level ADVANCED \
--language_out ES5 \
--js mylib/index.js
mylib/index.js:1:0: ERROR - [JSC_UNDEFINED_VARIABLE] variable module is undeclared
1| module.exports = {
^^^^^^
mylib/index.js:2:7: ERROR - [JSC_UNDEFINED_VARIABLE] variable require is undeclared
2| inc: require('./inc'),
^^^^^^^
2 error(s), 0 warning(s)
如果 CC 不了解module
並且require
這不是一個好的開始。
幸運的是,我們只缺少--process_common_js_modules
標志:
$ cc --compilation_level ADVANCED \
--language_out ES5 \
--process_common_js_modules \
--js mylib/index.js
mylib/index.js:2:7: ERROR - [JSC_JS_MODULE_LOAD_WARNING] Failed to load module "./inc"
2| inc: require('./inc'),
^
mylib/index.js:4:15: ERROR - [JSC_JS_MODULE_LOAD_WARNING] Failed to load module "./is_number"
4| is_number: require('./is_number')
^
2 error(s), 0 warning(s)
仍然不是很好,但這次錯誤有所不同:
require
我們需要--module_resolution
標志並告訴 CC 其他模塊在哪里:
$ cc --compilation_level ADVANCED \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--js mylib/index.js mylib/inc.js mylib/is_number.js
然而 output 是空的......
為什么? 在ADVANCED
編譯模式下,CC 會刪除任何未使用的代碼。 實際上是這樣的:到目前為止,所有這些東西都沒有被使用!
讓我們檢查一個不那么激進的編譯模式:
$ cc --compilation_level WHITESPACE_ONLY --formatting PRETTY_PRINT \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--js mylib/index.js mylib/inc.js mylib/is_number.js
var module$mylib$index = {default:{}};
module$mylib$index.default.inc = module$mylib$inc.default;
module$mylib$index.default.utils = {is_number:module$mylib$is_number.default};
var module$mylib$inc = {};
var is_number$$module$mylib$inc = module$mylib$is_number.default;
module$mylib$inc.default = function(x) {
return (0,module$mylib$is_number.default)(x) ? x + 1 : x;
};
var module$mylib$is_number = {};
module$mylib$is_number.default = function(x) {
return typeof x === "number";
};
我們可以看到,即使ADVANCED
編譯模式沒有刪除所有內容,這無論如何也不是很有用。 例如window.mylib
在哪里?
我設法在window.mylib
上獲得我的兩個庫並使用最激進的編譯模式編譯的唯一方法是為瀏覽器提供一個單獨的導出文件。
從這個mylib/index.js
module.exports = {
inc: require('./inc'),
utils: {
is_number: require('./is_number')
}
};
到這個mylib/index_browser.js
window.mylib = {
inc: require('./inc'),
utils: {
is_number: require('./is_number')
}
};
當您添加到window
object 時,CC 知道可能會到達此代碼,因此無法再安全地刪除它。
讓我們用這個文件再試一次:
$ cc --compilation_level ADVANCED --formatting PRETTY_PRINT \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--js mylib/index_browser.js mylib/inc.js mylib/is_number.js
function b(a) {
return "number" === typeof a;
}
;window.g = {h:function(a) {
return b(a) ? a + 1 : a;
}, j:{i:b}};
這看起來更好,但有一個主要問題:CC 已經破壞了所有名稱!
不用擔心。 我們只需要告訴 CC 應該不理會哪些名字。 這就是 externs 文件的目的。
mylib/externs.js
/** @externs */
var foo;
var inc;
var utils;
var is_number;
我們需要另一個標志:-- --externs
$ cc --compilation_level ADVANCED --formatting PRETTY_PRINT \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--externs mylib/externs.js \
--js mylib/index_browser.js mylib/inc.js mylib/is_number.js
function b(a) {
return "number" === typeof a;
}
;window.mylib = {inc:function(a) {
return b(a) ? a + 1 : a;
}, utils:{is_number:b}};
到達那里...
一個明顯的改進是將所有這些都包含在 IIFE 中,以避免不必要地污染全局 scope。
我們需要--isolation_mode
標志:
$ cc --compilation_level ADVANCED --formatting PRETTY_PRINT \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--externs mylib/externs.js \
--isolation_mode IIFE \
--js mylib/index_browser.js mylib/inc.js mylib/is_number.js
(function(){function b(a) {
return "number" === typeof a;
}
;window.mylib = {inc:function(a) {
return b(a) ? a + 1 : a;
}, utils:{is_number:b}};
}).call(this);
極好的!
剩下要做的就是將其保存到文件中並刪除格式以節省一些額外的字節:
$ cc --compilation_level ADVANCED \
--language_out ES5 \
--process_common_js_modules \
--module_resolution NODE \
--externs mylib/externs.js \
--isolation_mode IIFE \
--js mylib/index_browser.js mylib/inc.js mylib/is_number.js \
--js_output_file mylib/browser.min.js
mylib/browser.min.js
(function(){function b(a){return"number"===typeof a};window.mylib={inc:function(a){return b(a)?a+1:a},utils:{is_number:b}};}).call(this);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.