ReferenceError: WebSocket не определяется при использовании RxJs WebSocketSubject и Angular Universal

Я настраиваю универсальный проект angular 6.x, чтобы использовать его SSR (на стороне сервера Рендеринг) возможности. В своем приложении я использую связь через веб-сокеты с помощью RxJ.

В частности, я использую WebSocketSubject и webSocket в своем проекте angular universal 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

Мне удалось более точно решить проблему. Похоже, это тоже проблема webpack. Angular Universal создает пакет js для запуска приложения angular на сервере узла, но изначально на Node нет реализации веб-сокета. Следовательно, его нужно добавлять вручную как зависимость (пакет 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-файле решит проблему, но, конечно же, это взлом.

Итак, вопросы:

  • Могу ли я "заставить" webpack компилировать зависимость const WebSocket = require('ws'); => const WebSocket = require('ws'); (без изменений)?
  • Или, может быть, было бы лучшим решением зарегистрировать эту зависимость в универсальном пакете npm angular и создать запрос на перенос?

person Ronin    schedule 26.07.2018    source источник
comment
Разве это не вопрос проверки того, являетесь ли вы сервером или клиентом в своем коде, чтобы вы использовали сокеты, когда код выполняется в браузере?   -  person David    schedule 26.07.2018
comment
@David Есть аналогичный вопрос по SO (stackoverflow.com / questions / 41700412 /), который предлагает вызывать только веб-сокет на клиенте, что приводит к исчезновению ошибки, однако это не решение для меня. Я действительно хочу использовать веб-сокет, даже когда код выполняется на сервере.   -  person Ronin    schedule 27.07.2018
comment
Это то, что я имел в виду. Поскольку код выполняется на стороне сервера, а не на стороне клиента, зачем вам использовать серверную часть веб-сокетов?   -  person David    schedule 27.07.2018
comment
@David, чтобы он также был включен в страницу, отображаемую на стороне сервера. Если я сделаю http-запрос к любому маршруту в моем приложении angular, я хотел бы получить полную разметку для этого конкретного маршрута, поэтому он должен содержать даже данные веб-сокета.   -  person Ronin    schedule 02.08.2018


Ответы (4)


Все, что для этого нужно:

// set WebSocket on global object
(global as any).WebSocket = require('ws');

И, конечно же, нам нужна зависимость ws в package.json:

npm i ws -s

person Ronin    schedule 12.08.2018
comment
Привет, я использую angular 7, и я хочу использовать его с http, а не с ws или wss, как мне это сделать. - person ThinkTank; 18.03.2020

Функция RxJS webSocket может получать конструктор WebSocket в качестве аргумента. Это можно использовать для запуска webSocket в небраузерной / универсальной среде, подобной этой.

const WebSocketConstructor = WebSocket || require('ws')

const socket: WebSocketSubject<any> = webSocket({
    url: 'wss://echo.websocket.org', 
    WebSocketCtor: WebSocketConstructor,
});
person Bas    schedule 12.09.2019

В приложениях nodeJS с ES6 или аналогичными вы должны использовать это:

global.WebSocket = требуется ('WS')

Например:

import { WebSocketSubject, webSocket } from 'rxjs/webSocket';

global.WebSocket = require('ws'); // <-- FIX ALL ERRORS
const subject = webSocket('ws://...');

Больше никаких ошибок.

Мой первый ответ !! Ваше здоровье

person Emi Santi    schedule 19.11.2020

Я создал пакет npm, используя веб-сокеты. Чтобы обеспечить совместимость для js на стороне сервера и js браузера, я использую этот код.

(Примечание: это машинописный текст)

if (typeof global !== 'undefined') {
    (global as any).WebSocket = require('ws');
}

Как вы упомянули, браузер уже имеет WebSocket в своем глобальном объекте window, но node его нет. Поскольку в браузере нет global, эта простая проверка работает хорошо.

Скомпилированный код:

if (typeof global !== 'undefined') {
    global.WebSocket = require('ws');
}
person Konrad Klockgether    schedule 10.04.2019