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