打包具有依赖关系的Browser/Server CommonJS模块

Packaging-up Browser/Server CommonJS modules with dependancies

本文关键字:Server CommonJS 模块 Browser 包具 依赖 关系      更新时间:2024-02-09

假设我正在用JavaScript编写一个模块,该模块可以在浏览器和服务器上使用(带Node)。让我们称之为模块。假设Module将受益于另一个名为Dependency的模块中的方法。这两个模块都被编写为可供浏览器和服务器使用,类似于CommonJS风格:

module.js

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

dependency.js

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

显然,Dependency可以在浏览器中直接使用。但是,如果Module中包含var dependancy = require('dependency');指令,则"维护"该模块将变得更加麻烦。

我知道我可以在模块中执行依赖性的全局检查,如下所示:

var dependancy = this.Dependancy || require('dependancy');

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

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

添加这两个需求带来了像CommonJS这样易于使用的模块化框架的想法。

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

还有其他我没有想过的方法吗?

目前给出的两个答案都非常有用,帮助我找到了当前的解决方案。但是,根据我的评论,它们并不能完全满足我对可移植性和易用性的特殊要求(对于客户端和模块维护者来说)。

最后,我在browserfy命令行界面中发现了一个特定的标志,它可以捆绑模块并将它们公开为全局变量,并在RequireJS中使用(如果需要)。Browserify(和其他人)称之为通用模块定义(UMD)。这里有更多关于这方面的信息。

通过在browserfy命令中传递--standalone标志,我可以轻松地为UMD设置模块。

所以。。。

这是模块package.js

{
  "name": "module",
  "version": "0.0.1",
  "description": "My module that requires another module (dependancy)",
  "main": "index.js",
  "scripts": {
    "bundle": "browserify --standalone module index.js > module.js"
  },
  "author": "shennan",
  "devDependencies": {
    "dependancy": "*",
    "browserify": "*"
  }
}

因此,当在我的模块的根目录时,我可以在命令行中运行:

$ npm run-script bundle

它将依赖关系捆绑到一个文件中,并根据UMD方法公开它们。这意味着我可以通过三种不同的方式引导模块:

NodeJS

var Module = require('module');
/* use Module */

浏览器香草

<script src="module.js"></script>
<script>
  var Module = module;
  /* use Module */
</script>

带RequireJS的浏览器

<script src="require.js"></script>
<script>
  requirejs(['module.js'], function (Module) {
    /* use Module */
  });
</script>

再次感谢大家的投入。所有的答案都是有效的,我鼓励每个人都尝试一下,因为不同的用例需要不同的解决方案。

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

(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'
    };
});

这只是一个非常基本的示例——您可以返回函数、实例化函数或执行任何您喜欢的操作。在我的情况下,我将返回一个对象。

现在假设这是Dependency类。您的Module类看起来应该基本相同,它应该有一个对Dependency的依赖,比如:

function (require, exports, module) {
    var dependency = require('Dependency');
}

在RequireJS中,这被称为Simplified CommonJS Wrapper:http://requirejs.org/docs/api.html#cjsmodule

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

这里唯一的问题是保持文件的路径相同。请记住,嵌套的requires(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) { }
}

这对我来说非常有效。所有这些路径都有点棘手,但最终,你会将两个独立的模块放在不同的文件中,这些文件可以在两端使用,而无需任何类型的检查或破解-它们的所有依赖项都很有魅力:)

看看webpack bundler。您可以编写模块并通过模块导出进行导出。然后,您可以在服务器中使用module.export,并使用webpack进行浏览器构建。配置文件使用将是的最佳选择

module.exports = {
 entry: "./myModule",
 output: {
    path: "dist",
    filename: "myModule.js",
    library: "myModule",
    libraryTarget: "var"
 }
};

这将获取myModule并将其导出到myModule.js文件中。模块内部将被分配给名为myModule(库标志)的var(libraryTarget标志)。

可以导出为commonJS模块、war、this、function

由于绑定是节点脚本,因此可以按语法设置此标志值。

看看externals标志。如果您希望对某些依赖项具有特殊行为,则会使用它。例如,您正在创建react组件,并且在您的模块中您希望需要它,但在为web绑定时却不需要,因为它已经存在了。

希望这就是你想要的。