Как я могу совместно использовать код между Node.js и браузером?
Я создаю небольшое приложение с клиентом JavaScript (выполняется в браузере) и сервером Node.js, обмениваясь сообщениями с помощью WebSocket.
Я хотел бы поделиться кодом между клиентом и сервером. Я только что начал с Node.js, и мои знания современного JavaScript немного реже, мягко говоря. Таким образом, я все еще получаю свою голову вокруг функции CommonJS require(). Если я создаю свои пакеты с помощью объекта "export", то я не вижу, как я могу использовать одни и те же файлы JavaScript в браузере.
Я хочу создать набор методов и классов, которые используются на обоих концах для облегчения кодирования и декодирования сообщений и других зеркальных задач. Однако системы упаковки Node.js/CommonJS, по-видимому, не позволяют мне создавать файлы JavaScript, которые могут использоваться с обеих сторон.
Я также попытался использовать JS.Class, чтобы получить более жесткую OO-модель, но я отказался, потому что не мог понять, как получить предоставленные файлы JavaScript для работы с require(). Здесь что-то не хватает?
Ответы
Ответ 1
Если вы хотите написать модуль, который может использоваться как на стороне клиента, так и на стороне сервера, у меня есть короткое сообщение в блоге по быстрому и простому методу: Написание для 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 на стороне клиента, например Marak gemini.
Вы также можете быть заинтересованы в DNode, который позволяет вам выставить функцию JavaScript, чтобы ее можно было вызывать с другой машины, используя простой сетевой протокол на основе JSON.
Ответ 2
У 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();
Ответ 3
Проверьте исходный код 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)
Ответ 4
Не забывайте, что строковое представление функции JavaScript представляет собой исходный код для этой функции. Вы можете просто написать свои функции и конструкторы инкапсулированным способом, чтобы они могли быть toString() 'd и отправлены клиенту.
Другой способ сделать это - использовать систему сборки, поместить общий код в отдельные файлы, а затем включить их как на сервер, так и на клиентские скрипты. Я использую этот подход для простой клиент-серверной игры через WebSockets, где сервер и клиент выполняют по существу один и тот же игровой цикл, и клиент синхронизируется с сервером на каждый тик, чтобы убедиться, что никто не обманывает.
Моя система сборки для игры - это простой Bash script, который запускает файлы через препроцессор C, а затем через sed чтобы очистить некоторые нежелательные cpp файлы, я могу использовать все обычные файлы препроцессора, такие как #include, #define, #ifdef и т.д.
Ответ 5
Я бы рекомендовал изучить адаптер RequireJS для Node.js. Проблема заключается в том, что шаблон модуля Node.js для модели CommonJS по умолчанию не является асинхронным, что блокирует загрузку в веб-браузере. RequireJS использует шаблон AMD, который является одновременно асинхронным и совместимым с сервером и клиентом, если вы используете адаптер r.js
.
Ответ 6
Возможно, это не совсем соответствует этому вопросу, но я думал, что поделюсь этим.
Я хотел сделать пару простых функций командной строки, объявленных на String.prototype, доступных как для node, так и для браузера. Я просто сохраняю эти функции в файле с именем utilities.js(во вложенной папке) и легко ссылаюсь на него как из script -tag в моем коде браузера, так и с помощью require (исключая расширение .js) в моем Node.js script:
my_node_script.js
var utilities = require('./static/js/utilities')
my_browser_code.html
<script src="/static/js/utilities.js"></script>
Надеюсь, это полезная информация для кого-то, кроме меня.
Ответ 7
Сервер может просто отправлять исходные файлы 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" и возвращает его, чтобы вы могли назначить его там, где хотите, а не загрязнять глобальное пространство имен. В этом примере ему присваивается атрибут window fooModule
, который будет содержать код, экспортируемый файлом foo.js
.
Ответ 8
Ни одно из предыдущих решений не приносит системе модуля CommonJS браузер.
Как уже упоминалось в других ответах, существуют решения для управляющих активами/упаковщика, такие как Browserify или Piler, и есть RPC-решения, такие как dnode или nowjs.
Но я не смог найти реализацию CommonJS для браузера (включая функцию require()
и exports
/module.exports
и т.д.). Поэтому я написал свое собственное, чтобы потом узнать, что кто-то написал его лучше, чем я: https://github.com/weepy/brequire. Он называется Brequire (сокращение требуется для браузера).
Судя по популярности, управляющие активами отвечают потребностям большинства разработчиков. Однако, если вам нужна реализация браузера CommonJS, Brequire, вероятно, будет соответствовать счету.
2015 Обновление: Я больше не использую Brequire (он не обновлялся через несколько лет). Если я просто напишу небольшой модуль с открытым исходным кодом, и я хочу, чтобы кто-нибудь мог легко его использовать, тогда я буду следовать шаблону, подобному ответу Caolan (выше) - я написал сообщение в блоге об этом пару лет назад.
Однако, если я пишу модули для частного использования или для сообщества, стандартизованного в CommonJS (например, сообщество Ampersand) то я просто напишу их в формате CommonJS и использую Browserify.
Ответ 9
now.js тоже стоит посмотреть. Он позволяет вызывать серверную сторону с клиентской стороны, а клиентские функции - с серверной стороны
Ответ 10
Если вы хотите написать свой браузер в стиле Node.js-like, вы можете попробовать dualify.
Нет компиляции кода браузера, поэтому вы можете писать приложение без ограничений.
Ответ 11
Напишите ваш код как RequireJS модули и ваши тесты как Jasmine.
Этот способ можно загружать везде с помощью RequireJS, и тесты запускаются в браузере с помощью jasmine-html и jasmine-node в Node.js без необходимости изменения кода или тестов.
Вот рабочий пример для этого.
Ответ 12
Я написал это, его просто использовать, если вы хотите установить все переменные в глобальную область:
(function(vars, global) {
for (var i in vars) global[i] = vars[i];
})({
abc: function() {
...
},
xyz: function() {
...
}
}, typeof exports === "undefined" ? this : exports);
Ответ 13
Случай использования: поделитесь конфигурацией своего приложения между 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'
Ответ 14
Я написал простой модуль, который можно импортировать (либо использовать в узле, либо в теги сценария в браузере), который можно использовать для загрузки модулей как с клиента, так и с сервера.
Пример использования
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. Использование модуля
Поскольку это двусторонний загрузчик модулей, мы можем загружать его с обеих сторон (клиент и сервер). Поэтому вы можете сделать следующее, но вам не нужно делать оба сразу (не говоря уже о конкретном порядке):
В узле это просто:
var loader = require('./mloader.js');
loader.setRoot('./web');
var logModule = loader.importModuleSync('log2.js');
console.log(logModule.log2(4));
Это должно вернуть 2
.
Если ваш файл не находится в текущем каталоге узла, обязательно вызовите 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 (или, что бы вы ни http.server
сверхбыстрой, командной строки, решение для развертывания веб-сервера папки).
Если все будет хорошо, появится следующее:
![enter image description here]()
Ответ 15
Если вы используете утилиты модулей использования, такие как 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