"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');
}
Другие вопросы по тегам