"ReferenceError: WebSocket не определен" при использовании RxJs WebSocketSubject и Angular Universal
Я настраиваю угловой универсальный проект 6.x, чтобы использовать его возможности SSR (рендеринга на стороне сервера). В моем приложении я использую общение через веб-сокет с использованием RxJ.
Точнее, я использую WebSocketSubject
а также webSocket
в моем угловом универсальном проекте 6.x, который отлично работает на платформе браузера. Однако при запуске веб-сервера узла (который содержит компоненты SSR (рендеринг на стороне сервера)) выдается ошибка:
ReferenceError: WebSocket не определен
Пример кода:
// not actually code from the reproduction repo
import { WebSocketSubject, webSocket } from 'rxjs/webSocket';
const socket: WebSocketSubject<any> = webSocket('wss://echo.websocket.org');
socket.subscribe(msg => doSomething(msg));
Обратите внимание, что ошибка не возникает в браузерной версии приложения (т.е. ng serve
не выдаст ошибку), но только после компиляции SSR и запуска экспресс-сервера. Чтобы воспроизвести ошибку, сначала нужно запустить сборки:
# install dependencies
npm install
# build angular bundles for browser and server platforms
npm run build:client-and-server-bundles
# build the webserver
npm run webpack:server
# start the webserver
npm run serve:ssr
# the app is now being served at http://localhost:8081/
# open it in the browser and the error will occur: 'ReferenceError: WebSocket is not defined'
Я также создал репо репродукции.
Среда, которую я использую:
- Время выполнения: Angular 6
- Версия RxJS: 6.2.0, 6.2.1, 6.2.2 (проверено все)
Изменить 2018-08-02
Я смог решить проблему более точно. Похоже, это тоже проблема веб-пакета. Angular Universal создает пакет js для запуска приложения angular на сервере узлов, но в Node изначально нет реализации websocket. Поэтому его нужно добавить вручную как зависимость (пакет npm). Я попытался добавить его в комплект JS-сервера (const WebSocket = require('ws');
вручную, что решает проблему (т.е. ReferenceError исчезает). Однако, когда я добавлю его в код TypeScript, который позже будет транскомпилирован в пакет js, это не сработает.
Более подробная информация
- Загрузчик веб-пакетов
ts-loader
используется для компиляции TypeScript => JavaScript - Зависимость веб-сокета была добавлена в package.json:
"ws": "^6.0.0"
- Попытка сослаться на зависимость ws путем добавления
const WebSocket = require('ws');
к некомпилированному коду TypeScript не решит проблему. Это будет скомпилировано вvar WebSocket = __webpack_require__(333);
в выходном файле js зависимость не может быть разрешена. - Изменениевручную
var WebSocket = __webpack_require__(333);
=>const WebSocket = require('ws');
в скомпилированном js-файле это решило бы проблему, но, конечно, это взлом.
Итак, вопросы:
- Можно ли "заставить" веб-пакет скомпилировать зависимость?
const WebSocket = require('ws');
=>const WebSocket = require('ws');
(без изменений)? - Или, возможно, будет лучшим решением зарегистрировать эту зависимость в угловом универсальном пакете npm и создать запрос на извлечение?
1 ответ
Все, что нужно, это:
// set WebSocket on global object
(global as any).WebSocket = require('ws');
И, конечно же, нам нужно ws
Зависимость также в package.json:
npm i ws -s
Функция WebSocket RxJS может получать конструктор WebSocket в качестве аргумента. Это можно использовать для запуска webSocket в небраузерной / универсальной среде, подобной этой.
const WebSocketConstructor = WebSocket || require('ws')
const socket: WebSocketSubject<any> = webSocket({
url: 'wss://echo.websocket.org',
WebSocketCtor: WebSocketConstructor,
});
В приложениях nodeJS с ES6 или аналогичными вы должны использовать это:
global.WebSocket = требуется ('WS')
Например:
import { WebSocketSubject, webSocket } from 'rxjs/webSocket';
global.WebSocket = require('ws'); // <-- FIX ALL ERRORS
const subject = webSocket('ws://...');
Больше никаких ошибок.
Мой первый ответ!! Ура
Я создал пакет npm, используя websockets. Для обеспечения совместимости js на стороне сервера и js браузера я использую этот код.
(Примечание: это машинопись)
if (typeof global !== 'undefined') {
(global as any).WebSocket = require('ws');
}
Как вы уже упоминали, в глобальном объекте браузера уже есть WebSocket window
но узел не делает. Так как браузер не имеет global
Эта простая проверка работает хорошо.
Скомпилированный код:
if (typeof global !== 'undefined') {
global.WebSocket = require('ws');
}