使用 RxJs WebSocketSubject 和 Angular Universal 时出现“ReferenceError: WebSocket is not defined”

Posted

技术标签:

【中文标题】使用 RxJs WebSocketSubject 和 Angular Universal 时出现“ReferenceError: WebSocket is not defined”【英文标题】:"ReferenceError: WebSocket is not defined" when using RxJs WebSocketSubject and Angular Universal 【发布时间】:2019-01-03 01:20:19 【问题描述】:

我正在设置 angular 6.x univeral project 以利用其 s-s-r(服务器端渲染)功能。在我的应用程序中,我使用 RxJs 使用 websocket 通信。

更具体地说,我在我的 Angular 通用 6.x 项目中使用 WebSocketSubjectwebSocket,这在浏览器平台上运行良好。但是,在运行节点 Web 服务器(包含 s-s-r 内容(服务器端渲染))时,会引发错误:

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 不会抛出错误),但只有在编译 s-s-r 内容并运行快递网络服务器。要重现错误,必须先运行构建:

# 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:s-s-r

# 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'

我还设置了reproduction repo。

我正在使用的环境:

运行时:Angular 6 RxJS 版本:6.2.0、6.2.1、6.2.2(全部测试)

编辑 2018-08-02

我能够更准确地解决问题。看来,这也是一个 webpack 的问题。 Angular Universal 创建了一个 js 包,用于在节点服务器上运行 Angular 应用程序,但 Node 上本身没有 websocket 实现。因此,必须手动将其添加为依赖项(npm 包)。我尝试将它添加到 js 服务器包中(手动添加const WebSocket = require('ws');解决了问题(即 ReferenceError 消失了)。但是,当我将它添加到 TypeScript 代码中时,该代码稍后会被转编译到 js 包中开,它不会工作。

更多详情

webpack loader ts-loader 用于编译 TypeScript => javascript 在 package.json 中添加了 websocket 依赖:"ws": "^6.0.0" 尝试通过将const WebSocket = require('ws'); 添加到未编译的 TypeScript 代码来引用 ws 依赖项将无法解决问题。会在js输出文件中编译成var WebSocket = __webpack_require__(333);,无法解析依赖。 手动在编译的 js 文件中更改 var WebSocket = __webpack_require__(333); => const WebSocket = require('ws');会解决问题,但当然这是一个 hack。

所以,问题是:

我可以“强制”webpack 编译依赖项const WebSocket = require('ws'); => const WebSocket = require('ws');(无更改)吗? 或者,在 Angular 通用 npm 包中注册此依赖项并创建拉取请求可能是更好的解决方案?

【问题讨论】:

在你的代码中是不是检查你是服务器端还是客户端,以便在浏览器中执行代码时使用套接字? @David 在 SO (***.com/questions/41700412/…) 上有一个类似的问题,建议只在客户端调用 websocket,这会使错误消失,但是,这不是我的解决方案。即使在服务器上执行代码,我也很想使用websocket。 这就是我的意思。既然代码是在服务端执行的,然后是客户端执行的,为什么还要使用 websockets 服务端呢? @David 所以它也被包含在服务器端呈现的页面中。如果我向我的 Angular 应用程序中的任何路由发出 http 请求,我想获得该特定路由的完整标记,因此它甚至应该包含 websocket 数据。 【参考方案1】:

只需要这样:

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

当然,我们还需要 package.json 中的 ws 依赖项:

npm i ws -s

【讨论】:

您好,我正在使用 Angular 7,我想将它与 http 一起使用,而不是与 ws 或 wss 我如何完成它。【参考方案2】:

我使用 websockets 创建了一个 npm 包。 为了提供服务器端 js 和浏览器 js 的兼容性,我使用此代码。

(注意:是打字稿)

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

就像你提到的,浏览器在其全局对象window 中已经有一个 WebSocket,但节点没有。 由于浏览器没有global,所以这个简单的检查效果很好。

编译代码:

if (typeof global !== 'undefined') 
    global.WebSocket = require('ws');

【讨论】:

【参考方案3】:

RxJS webSocket 函数可以接收一个 WebSocket 构造函数作为参数。这可以用来在像这样的非浏览器/通用环境中运行 webSocket。

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

const socket: WebSocketSubject<any> = webSocket(
    url: 'wss://echo.websocket.org', 
    WebSocketCtor: WebSocketConstructor,
);

【讨论】:

【参考方案4】:

在 ES6 或类似的 nodeJS 应用中,你必须使用这个:

global.WebSocket = require('ws')

例如:

import  WebSocketSubject, webSocket  from 'rxjs/webSocket';

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

没有更多错误。

我的第一个答案!!干杯

【讨论】:

以上是关于使用 RxJs WebSocketSubject 和 Angular Universal 时出现“ReferenceError: WebSocket is not defined”的主要内容,如果未能解决你的问题,请参考以下文章

WebSocketSubject 不工作

WebSocketSubject - 优势?

从 WebSocketSubject 反序列化 Blob 对象

Angular2 - 如何关闭 WebSocketSubject 底层套接字

从组件到服务的角度9传递方法

如何理解 RxJS?RxJS的中文API和使用教程