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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3225251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:45:33  来源:igfitidea点击:

How can I share code between Node.js and the browser?

javascriptnode.js

提问by Simon Cave

I am creating a small application with a JavaScript client (run in the browser) and a Node.js server, communicating using WebSocket.

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

I would like to share code between the client and the server. I have only just started with Node.js and my knowledge of modern JavaScript is a little rusty, to say the least. So I am still getting my head around the CommonJS require() function. If I am creating my packages by using the 'export' object, then I cannot see how I could use the same JavaScript files in the browser.

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

I want to create a set of methods and classes that are used on both ends to facilitate encoding and decoding messages, and other mirrored tasks. However, the Node.js/CommonJS packaging systems seems to preclude me from creating JavaScript files that can be used on both sides.

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

I also tried using JS.Class to get a tighter OO model, but I gave up because I couldn't figure out how to get the provided JavaScript files to work with require(). Is there something am I missing here?

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

采纳答案by Caolan

If you want to write a module that can be used both client side and server side, I have a short blog post on a quick and easy method: Writing for Node.js and the browser, essentially the following (where thisis the same as window):

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

(function(exports){

    // Your code goes here

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

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

Alternatively there are some projects aiming to implement the Node.js API on the client side, such as Marak's gemini.

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

You might also be interested in DNode, which lets you expose a JavaScript function so that it can be called from another machine using a simple JSON-based network protocol.

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

回答by broesch

Epeli has a nice solution here http://epeli.github.com/piler/that even works without the library, just put this in a file called share.js

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

On the server side just use:

在服务器端只需使用:

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

share.test();

And on the client side just load the js file and then use

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

share.test();

回答by wlingke

Checkout the jQuery source code that makes this work in the Node.js module pattern, AMD module pattern, and global in the browser:

查看在 Node.js 模块模式、AMD 模块模式和浏览器中全局执行此操作的 jQuery 源代码:

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

回答by Husky

I would recommend looking into the RequireJS adapter for Node.js. The problem is that the CommonJS module pattern Node.js uses by default isn't asynchronous, which blocks loading in the web browser. RequireJS uses the AMD pattern, which is both asynchronous and compatible with both server and client, as long as you use the r.jsadapter.

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

回答by Dagg Nabbit

Don't forget that the string representation of a JavaScript function represents the source code for that function. You could simply write your functions and constructors in an encapsulated way so they can be toString()'d and sent to the client.

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

Another way to do it is use a build system, put the common code in separate files, and then include them in both the server and client scripts. I'm using that approach for a simple client/server game via WebSockets where the server and client both run essentially the same game loop and the client synchronises up with the server every tick to make sure nobody's cheating.

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

My build system for the game is a simple Bashscript that runs the files through the C preprocessor and then through sed to clean up some junk cpp leaves behind, so I can use all the normal preprocessor stuff like #include, #define, #ifdef, etc.

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

回答by Markus Amalthea Magnuson

Maybe this is not entirely in line with the question, but I thought I'd share this.

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

I wanted to make a couple of simple string utility functions, declared on String.prototype, available to both node and the browser. I simply keep these functions in a file called utilities.js (in a subfolder) and can easily reference it both from a script-tag in my browser code, and by using require (omitting the .js extension) in my Node.js script:

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

my_node_script.js

my_node_script.js

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

my_browser_code.html

my_browser_code.html

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

I hope this is useful information to someone other than me.

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

回答by Yuci

If you use use module bundlers such as webpackto bundle JavaScript files for usage in a browser, you can simply reuse your Node.js module for the frontend running in a browser. In other words, your Node.js module can be shared between Node.js and the browser.

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

For example, you have the following code sum.js:

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

Normal Node.js module: sum.js

普通 Node.js 模块:sum.js

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

module.exports = sum

Use the module in Node.js

使用 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

Reuse it in the frontend

在前端重用它

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

回答by maerics

The server can simply send JavaScript source files to the client (browser) but the trick is that the client will have to provide a mini "exports" environment before it can execthe code and store it as a module.

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

A simple way to make such an environment is to use a closure. For example, say your server provides source files via HTTP like http://example.com/js/foo.js. The browser can load the required files via an XMLHttpRequest and load the code like so:

制作这种环境的一种简单方法是使用闭包。例如,假设您的服务器通过 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);
  }
});

The key is that client can wrap the foreign code into an anonymous function to be run immediately (a closure) which creates the "exports" object and returns it so you can assign it where you'd like, rather than polluting the global namespace. In this example, it is assigned to the window attribute fooModulewhich will contain the code exported by the file foo.js.

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

回答by Peter Rust

None of the previous solutions bring the CommonJS module system to the browser.

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

As mentioned in the other answers, there are asset manager/packager solutions like Browserifyor Pilerand there are RPC solutions like dnodeor nowjs.

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

But I couldn't find an implementation of CommonJS for the browser (including a require()function and exports/ module.exportsobjects, etc.). So I wrote my own, only to discover afterwards that someone else had written it better than I had: https://github.com/weepy/brequire. It's called Brequire (short for Browser require).

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

Judging by popularity, asset managers fit the needs of most developers. However, if you need a browser implementation of CommonJS, Brequirewill probably fit the bill.

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

2015 Update:I no longer use Brequire (it hasn't been updated in a few years). If I'm just writing a small, open-source module and I want anyone to be able to easily use, then I'll follow a pattern similar to Caolan's answer (above) -- I wrote a blog postabout it a couple years ago.

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

However, if I'm writing modules for private use or for a community that is standardized on CommonJS (like the Ampersandcommunity) then I'll just write them in CommonJS format and use Browserify.

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

回答by farincz

If you want to write your browser in Node.js-like style you can try dualify.

如果你想用类似 Node.js 的风格编写你的浏览器,你可以尝试dualify

There is no browser code compilation, so you can write your application without limitations.

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