Как я могу поделиться кодом между Node.js и браузером? - PullRequest
233 голосов
/ 12 июля 2010

Я создаю небольшое приложение с клиентом JavaScript (запускается в браузере) и сервером Node.js, общение с помощью WebSocket.

Я хотел бы поделиться кодом между клиентом и сервером. Я только начал с Node.js, и мои знания современного JavaScript немного устарели, если не сказать больше. Так что я все еще думаю о функции CommonJS require (). Если я создаю свои пакеты с помощью объекта «export», я не вижу, как я могу использовать те же файлы JavaScript в браузере.

Я хочу создать набор методов и классов, которые используются на обоих концах для облегчения кодирования и декодирования сообщений и других зеркальных задач. Однако системы упаковки Node.js / CommonJS, по-видимому, не позволяют мне создавать файлы JavaScript, которые можно использовать с обеих сторон.

Я также пытался использовать JS.Class, чтобы получить более узкую модель OO, но я сдался, потому что не мог понять, как заставить предоставленные файлы JavaScript работать с require (). Я что-то упускаю здесь?

Ответы [ 15 ]

164 голосов
/ 12 июля 2010

Если вы хотите написать модуль, который можно использовать как на стороне клиента, так и на стороне сервера, у меня есть короткое сообщение в блоге о быстром и простом методе: Написание для Node.js и браузера , по существу, следующее (где this совпадает с window):

(function(exports){

    // Your code goes here

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

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

В качестве альтернативы существуют некоторые проекты, направленные на реализацию API Node.js на стороне клиента, напримерas Близнецы Марака .

Возможно, вас заинтересует DNode , который позволяет вам предоставлять функцию JavaScript, чтобы ее можно было вызывать с другого компьютера с помощью простого JSON.сетевой протокол.

39 голосов
/ 07 декабря 2011

У 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();
14 голосов
/ 20 мая 2014

Извлеките исходный код 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)
13 голосов
/ 30 июня 2011

Я бы порекомендовал заглянуть в RequireJS адаптер для Node.js .Проблема состоит в том, что шаблон модуля CommonJS, используемый по умолчанию Node.js, не является асинхронным, что блокирует загрузку в веб-браузере.RequireJS использует шаблон AMD, который является асинхронным и совместимым как с сервером, так и с клиентом, если вы используете адаптер r.js.

13 голосов
/ 12 июля 2010

Не забывайте, что строковое представление функции JavaScript представляет исходный код этой функции. Вы можете просто написать свои функции и конструкторы инкапсулированным способом, чтобы они могли быть toString () 'd и отправлены клиенту.

Еще один способ сделать это - использовать систему сборки, поместить общий код в отдельные файлы, а затем включить их в сценарии сервера и клиента. Я использую этот подход для простой клиент-серверной игры через WebSockets, где сервер и клиент выполняют по существу один и тот же игровой цикл, а клиент синхронизируется с сервером каждый тик, чтобы никто не обманывал.

Моя система сборки для игры представляет собой простой Bash скрипт, который запускает файлы через препроцессор C, а затем через sed, чтобы очистить некоторые ненужные cpp-листы, так что я могу использовать все обычные препроцессоры как #include, #define, #ifdef и т. д.

11 голосов
/ 29 июля 2012

Возможно, это не совсем соответствует вопросу, но я решил поделиться этим.

Я хотел сделать пару простых строковых утилит, объявленных в String.prototype, доступными как для узла, так и для браузера. Я просто храню эти функции в файле с именем utilities.js (во вложенной папке) и могу легко ссылаться на него как из тега script в коде моего браузера, так и с помощью require (без расширения .js) в моем скрипте Node.js :

my_node_script.js

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

my_browser_code.html

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

Я надеюсь, что это полезная информация для кого-то, кроме меня.

5 голосов
/ 02 декабря 2018

Если вы используете комплекты модулей, такие как webpack , для объединения файлов JavaScript для использования в браузере, вы можете просто повторно использовать модуль Node.js для интерфейса, работающего в браузере , Другими словами, ваш модуль Node.js может использоваться совместно Node.js и браузером.

Например, у вас есть следующий код sum.js:

Обычный модуль 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
4 голосов
/ 12 июля 2010

Сервер может просто отправлять исходные файлы 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);
  }
});

Ключ заключается в том, что клиент может обернуть внешний код в анонимную функцию, которая будет немедленно запущена (замыкание), которая создает объект «export» и возвращает его, чтобы вы могли назначить его где угодно, а не загрязнять глобальное пространство имен. В этом примере он назначен атрибуту окна fooModule, который будет содержать код, экспортированный файлом foo.js.

2 голосов
/ 15 июня 2012

Ни одно из предыдущих решений не выводило модульную систему CommonJS в браузер.

Как уже упоминалось в других ответах, существуют решения для управления активами / упаковщики, такие как Browserify или Piler , и существуют решения RPC, такие как dnode или nowjs .

Но я не смог найти реализацию CommonJS для браузера (включая функцию require() и exports / module.exports объекты и т. Д.). Поэтому я написал свой собственный, но потом обнаружил, что кто-то другой написал его лучше, чем я: https://github.com/weepy/brequire. Это называется Brequire (сокращение от Browser require).

Судя по популярности, управляющие активами соответствуют потребностям большинства разработчиков. Тем не менее, если вам нужна браузерная реализация CommonJS, Brequire , вероятно, подойдет.

2015 Обновление: Я больше не использую Brequire (он не обновлялся в течение нескольких лет). Если я просто пишу небольшой модуль с открытым исходным кодом и хочу, чтобы кто-нибудь мог его легко использовать, то я буду следовать схеме, аналогичной ответу Каолана (выше) - я написал сообщение в блоге об этом пару лет назад.

Однако, если я пишу модули для частного использования или для сообщества, которое стандартизировано в CommonJS (например, сообщество Ampersand ), тогда я просто напишу их в формате CommonJS и использую Browserify .

1 голос
/ 13 октября 2018

Я написал простой модуль , который можно импортировать (используя require в Node или теги script в браузере), который можно использовать для загрузки модулей как с клиента, так и с сервера.

Пример использования

1. Определение модуля

Поместите следующее в файл log2.js внутри вашей папки статических веб-файлов:

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 с указанием пути к вашей папке статических веб-файлов (или там, где находится ваш модуль).

  • В браузере:

Сначала определите веб-страницу:

<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, я предлагаю вам взглянуть на модуль http.server в Python 3 (или каково бы ни было ваше решение для развертывания суперскоростного сервера, командной строки, веб-сервера папок).

Если все пойдет хорошо, появится следующее:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...