封装浏览器/服务器具有依赖性的CommonJS模块

she*_*nan 6 javascript module commonjs node.js browserify

假设我正在用JavaScript编写一个模块,可以在浏览器和服务器上使用(使用Node).让我们称之为模块.并且假设该模块将受益于另一个名为Dependancy的模块中的方法.这两个模块都已编写为浏览器和服务器使用,即CommonJS样式:

module.js

if (typeof module !== 'undefined' && module.exports)
  module.exports = Module; /* server */
else
  this.Module = Module; /* browser */
Run Code Online (Sandbox Code Playgroud)

dependancy.js

if (typeof module !== 'undefined' && module.exports)
  module.exports = Dependancy; /* server */
else
  this.Dependancy = Dependancy; /* browser */
Run Code Online (Sandbox Code Playgroud)

显然,Dependancy可以在浏览器中直接使用.但是如果Module中包含一个var dependancy = require('dependency');指令,那么"维护"模块就变得更加麻烦.

我知道我能为执行全局检查扶养模块,就像这样:

var dependancy = this.Dependancy || require('dependancy');
Run Code Online (Sandbox Code Playgroud)

但这意味着我的模块有两个额外的浏览器安装要求:

  • 用户必须在他们的文档中包含dependency.js文件<script>
  • 并且用户必须确保在module.js之前加载此脚本

添加这两个要求会引发像CommonJS这样易于使用的模块化框架.

对我来说另一个选择是我在我的Module包中包含第二个已编译的脚本,并dependency.js使用browserify捆绑.然后,我指示在浏览器中使用该脚本的用户包含此脚本,而服务器端用户使用该中列出的未捆绑的条目脚本package.json.这比第一种方式更好,但它需要一个预编译过程,每次我更改库时都要运行(例如,在上传到GitHub之前).

有没有其他方法可以做到这一点,我没有想到?

And*_*pov 1

当然,您可以使用双方都依赖的相同模块。您只需要更好地指定它即可。这是我使用的方式:

(function (name, definition){
    if (typeof define === 'function'){ // AMD
        define(definition);
    } else if (typeof module !== 'undefined' && module.exports) { // Node.js
        module.exports = definition();
    } else { // Browser
        var theModule = definition(), global = this, old = global[name];
        theModule.noConflict = function () {
            global[name] = old;
            return theModule;
        };

        global[name] = theModule;
    }
})('Dependency', function () {
    // return the module's API
    return {
        'key': 'value'
    };
});
Run Code Online (Sandbox Code Playgroud)

这只是一个非常基本的示例 - 您可以返回函数、实例化函数或执行任何您喜欢的操作。就我而言,我正在返回一个对象。

现在假设这就是课程Dependency。您的Module类应该看起来几乎相同,它应该有一个依赖项Dependency

function (require, exports, module) {
    var dependency = require('Dependency');
}
Run Code Online (Sandbox Code Playgroud)

在 RequireJS 中,这称为简化 CommonJS 包装器:http ://requirejs.org/docs/api.html#cjsmodule

require因为代码开头有一条语句,所以它将作为依赖项进行匹配,因此它将被延迟加载,或者如果您对其进行优化 - 尽早标记为依赖项(它将自动转换define(definition)define(['Dependency'], definition)依赖项)。

这里唯一的问题是保持文件的路径相同。请记住,嵌套的 require (if-else) 在 Require 中不起作用(阅读文档),所以我必须这样做:

var dependency;
try {
    dependency = require('./Dependency'); // node module in the same folder
} catch(err) { // it's not node
    try {
        dependency = require('Dependency'); // requirejs
    } catch(err) { }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说非常有效。所有这些路径都有点棘手,但最终,您会在不同的文件中获得两个单独的模块,这些模块可以在两端使用,而无需任何类型的检查或黑客攻击 - 它们的所有依赖项都像一个魅力 :)