簡體   English   中英

如何使用 Google Closure Compiler 瀏覽您的 Node.js 庫

[英]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

TL; 博士

  1. 創建mylib/index_browser.js

     window.mylib = { inc: require('./inc'), utils: { is_number: require('./is_number') } };
  2. 創建mylib/externs.js

     /** @externs */ var mylib; var inc; var utils; var is_number;
  3. 然后:

     $ 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)

仍然不是很好,但這次錯誤有所不同:

  1. CC不知道你說的是哪個require
  2. CC 不知道這兩個其他模塊在哪里

我們需要--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.

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