如何在Node.js和浏览器之间共享代码?

javascript node.js

67453 观看

15回复

我正在使用JavaScript客户端(在浏览器中运行)和Node.js服务器创建一个小应用程序,使用WebSocket进行通信。

我想在客户端和服务器之间共享代码。我刚刚开始使用Node.js,至少可以说,我对现代JavaScript的了解有点生疏。所以我仍然围绕CommonJS的require()函数。如果我使用'export'对象创建我的包,那么我无法看到如何在浏览器中使用相同的JavaScript文件。

我想创建一组在两端使用的方法和类,以便于编码和解码消息,以及其他镜像任务。但是,Node.js / CommonJS打包系统似乎阻止我创建可以在双方使用的JavaScript文件。

我也尝试使用JS.Class来获得更紧密的OO模型,但我放弃了,因为我无法弄清楚如何让提供的JavaScript文件与require()一起使用。这里有什么我想念的吗?

作者: Simon Cave 的来源 发布者: 2019 年 6 月 8 日

回应 (15)


161

决定

如果你想编写一个可以在客户端和服务器端使用的模块,我有一个简短的博客文章,快速简单的方法:编写Node.js和浏览器,基本上如下(其中this相同window) :

(function(exports){

    // Your code goes here

   exports.test = function(){
        return 'hello world'
    };

})(typeof exports === 'undefined'? this['mymodule']={}: exports);

或者,有一些项目旨在在客户端实现Node.js API,例如Marak的gemini

您可能还对DNode感兴趣,它允许您公开JavaScript函数,以便可以使用简单的基于JSON的网络协议从另一台计算机调用它。

作者: Caolan 发布者: 12.07.2010 08:00

39

Epeli在这里提供了一个很好的解决方案http://epeli.github.com/piler/即使没有库也可以工作,只需将其放在名为share.js的文件中即可。

(function(exports){

  exports.test = function(){
       return 'This is a function from shared module';
  };

}(typeof exports === 'undefined' ? this.share = {} : exports));

在服务器端只需使用:

var share = require('./share.js');

share.test();

在客户端只需加载js文件然后使用

share.test();
作者: broesch 发布者: 07.12.2011 06:58

14

检查jQuery源代码,使其在Node.js模块模式,AMD模块模式和浏览器中的全局工作:

(function(window){
    var jQuery = 'blah';

    if (typeof module === "object" && module && typeof module.exports === "object") {

        // Expose jQuery as module.exports in loaders that implement the Node
        // module pattern (including browserify). Do not create the global, since
        // the user will be storing it themselves locally, and globals are frowned
        // upon in the Node module world.
        module.exports = jQuery;
    }
    else {
        // Otherwise expose jQuery to the global object as usual
        window.jQuery = window.$ = jQuery;

        // Register as a named AMD module, since jQuery can be concatenated with other
        // files that may use define, but not via a proper concatenation script that
        // understands anonymous AMD modules. A named AMD is safest and most robust
        // way to register. Lowercase jquery is used because AMD module names are
        // derived from file names, and jQuery is normally delivered in a lowercase
        // file name. Do this after creating the global so that if an AMD module wants
        // to call noConflict to hide this version of jQuery, it will work.
        if (typeof define === "function" && define.amd) {
            define("jquery", [], function () { return jQuery; });
        }
    }
})(this)
作者: wlingke 发布者: 20.05.2014 03:52

13

不要忘记JavaScript函数的字符串表示形式表示该函数的源代码。您可以简单地以封装的方式编写函数和构造函数,以便它们可以是toString()并发送到客户端。

另一种方法是使用构建系统,将公共代码放在单独的文件中,然后将它们包含在服务器和客户端脚本中。我正在通过WebSockets将这种方法用于简单的客户端/服务器游戏,其中服务器和客户端基本上运行相同的游戏循环,并且客户端每次打勾都与服务器同步以确保没有人作弊。

我的游戏构建系统是一个简单的Bash脚本,它通过C预处理器运行文件,然后通过sed来清理一些垃圾cpp离开,所以我可以使用所有正常的预处理器内容,如#include,#define,#ifdef等

作者: Dagg Nabbit 发布者: 12.07.2010 01:19

13

我建议查看Node.jsRequireJS适配器。问题是默认情况下Node.js使用的CommonJS模块模式不是异步的,这会阻止在Web浏览器中加载。RequireJS使用AMD模式,只要您使用r.js适配器,它就是异步的并且兼容服务器和客户端。

作者: Husky 发布者: 30.06.2011 01:55

11

也许这不完全符合这个问题,但我想我会分享这个。

我想制作几个简单的字符串实用程序函数,在String.prototype上声明,可用于节点和浏览器。我只是将这些函数保存在一个名为utilities.js的文件中(在子文件夹中),并且可以从我的浏览器代码中的脚本标记和我的Node.js脚本中使用require(省略.js扩展名)轻松引用它。 :

my_node_script.js

var utilities = require('./static/js/utilities')

my_browser_code.html

<script src="/static/js/utilities.js"></script>

我希望这对我以外的人有用。

作者: Markus Amalthea Magnuson 发布者: 29.07.2012 12:03

4

服务器可以简单地将JavaScript源文件发送到客户端(浏览器),但诀窍是客户端必须提供迷你“导出”环境才能exec将代码存储为模块。

制作这种环境的一种简单方法是使用闭包。例如,假设您的服务器通过HTTP提供源文件http://example.com/js/foo.js。浏览器可以通过XMLHttpRequest加载所需的文件并加载代码,如下所示:

ajaxRequest({
  method: 'GET',
  url: 'http://example.com/js/foo.js',
  onSuccess: function(xhr) {
    var pre = '(function(){var exports={};'
      , post = ';return exports;})()';
    window.fooModule = eval(pre + xhr.responseText + post);
  }
});

关键是客户端可以将外部代码包装成一个匿名函数,以便立即运行(一个闭包),它创建“exports”对象并返回它,以便您可以将它分配到您想要的位置,而不是污染全局命名空间。在此示例中,它被分配给window属性fooModule,该属性将包含文件导出的代码foo.js

作者: maerics 发布者: 12.07.2010 01:32

2

以前的解决方案都没有将CommonJS模块系统引入浏览器。

正如在其他的答案中提到,有喜欢的资产管理公司/打包解决方案Browserify堆垛机和有像RPC解决方案dnodenowjs

但我找不到浏览器的CommonJS实现(包括require()函数和exports/ module.exports对象等)。所以我写了自己的,但之后发现其他人写的比我更好:https//github.com/weepy/brequire。它被称为Brequire(浏览器要求的缩写)。

从受欢迎程度来看,资产管理者满足大多数开发人员的需求。但是,如果您需要浏览器实现CommonJS,Brequire可能会适合该法案。

2015年更新:我不再使用Brequire(它在几年内没有更新)。如果我只是在编写一个小型的开源模块,并且我希望任何人能够轻松使用,那么我将遵循类似于Caolan的答案的模式(上图) - 我写了一篇关于它的博客文章几年前。

但是,如果我正在编写供私人使用的模块或者在CommonJS上标准化的社区(如Ampersand社区),那么我只需用CommonJS格式编写它们并使用Browserify

作者: Peter Rust 发布者: 14.06.2012 11:51

1

now.js也值得一看。它允许您从客户端调用服务器端,从服务器端调用客户端功能

作者: balupton 发布者: 20.09.2011 10:21

1

如果您想以类似Node.js的方式编写浏览器,可以尝试使用dualify

没有浏览器代码编译,因此您可以无限制地编写应用程序。

作者: farincz 发布者: 27.03.2013 11:06

1

将代码编写为RequireJS模块,将测试编写为Jasmine测试。

这样,代码可以使用RequireJS在任何地方加载,测试可以在浏览器中使用jasmine-html和Node.js中的jasmine-node运行,而无需修改代码或测试。

这是一个有效的例子

作者: Blacksonic 发布者: 08.09.2013 03:12

0

我写了这个,如果你想将所有变量设置为全局范围,它很容易使用:

(function(vars, global) {
    for (var i in vars) global[i] = vars[i];
})({
    abc: function() {
        ...
    },
    xyz: function() {
        ...
    }
}, typeof exports === "undefined" ? this : exports);
作者: super 发布者: 27.02.2016 04:18

0

使用案例:在Node.js和浏览器之间共享您的应用程序配置(这只是一个例子,可能不是最好的方法,具体取决于您的应用程序)。

问题:您不能使用window(在Node.js中不存在),也不能global(在浏览器中不存在)。

解:

  • 文件config.js:

    var config = {
      foo: 'bar'
    };
    if (typeof module === 'object') module.exports = config;
    
  • 在浏览器中(index.html):

    <script src="config.js"></script>
    <script src="myApp.js"></script>
    

    您现在可以打开开发工具并访问全局变量 config

  • 在Node.js(app.js)中:

    const config = require('./config');
    console.log(config.foo); // Prints 'bar'
    
  • 使用Babel或TypeScript:

    import config from './config';
    console.log(config.foo); // Prints 'bar'
    
作者: tanguy_k 发布者: 01.10.2018 11:19

0

我编写了一个简单的模块,可以导入(使用Node中的require或浏览器中的脚本标记),您可以使用它从客户端和服务器加载模块。

用法示例

1.定义模块

将以下内容log2.js放在静态Web文件文件夹中的文件中:

let exports = {};

exports.log2 = function(x) {
    if ( (typeof stdlib) !== 'undefined' )
        return stdlib.math.log(x) / stdlib.math.log(2);

    return Math.log(x) / Math.log(2);
};

return exports;

就那么简单!

2.使用模块

由于它是双边模块加载器,我们可以从双方(客户端和服务器)加载它。因此,您可以执行以下操作,但不需要同时执行这两项操作(更不用说按特定顺序执行):

  • 在节点中

在Node中,它很简单:

var loader = require('./mloader.js');
loader.setRoot('./web');

var logModule = loader.importModuleSync('log2.js');
console.log(logModule.log2(4));

这应该回来了2

如果您的文件不在Node的当前目录中,请确保loader.setRoot使用静态Web文件文件夹的路径(或模块所在的位置)进行调用。

  • 在浏览器中:

首先,定义网页:

<html>
    <header>
        <meta charset="utf-8" />
        <title>Module Loader Availability Test</title>

        <script src="mloader.js"></script>
    </header>

    <body>
        <h1>Result</h1>
        <p id="result"><span style="color: #000088">Testing...</span></p>

        <script>
            let mod = loader.importModuleSync('./log2.js', 'log2');

            if ( mod.log2(8) === 3 && loader.importModuleSync('./log2.js', 'log2') === mod )
                document.getElementById('result').innerHTML = "Your browser supports bilateral modules!";

            else
                document.getElementById('result').innerHTML = "Your browser doesn't support bilateral modules.";
        </script>
    </body>
</html>

确保不要直接在浏览器中打开文件; 因为它使用AJAX,我建议你看一下Python 3的http.server模块(或者你的超高速,命令行,文件夹web服务器部署解决方案)。

如果一切顺利,将出现:

在此输入图像描述

作者: Gustavo6046 发布者: 13.10.2018 08:00

0

如果您使用诸如webpack之类的模块捆绑包来捆绑JavaScript文件以便在浏览器中使用,则可以简单地将Node.js模块重用于在浏览器中运行的前端。换句话说,您的Node.js模块可以在Node.js和浏览器之间共享。

例如,您有以下代码sum.js:

Normal Node.js模块:sum.js

const sum = (a, b) => {
    return a + b
}

module.exports = sum

在Node.js中使用该模块

const sum = require('path-to-sum.js')
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

在前端重复使用它

import sum from 'path-to-sum.js'
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7
作者: Yuci 发布者: 02.12.2018 01:26
32x32