问题
I'm setting up an angular 6.x univeral project in order to leverage its SSR (Server-Side Rendering) capabilities. In my app, I'm using websocket communication using RxJs.
More specifically, I'm, using WebSocketSubject
and webSocket
in my angular universal 6.x project, which works fine on the browser platform. However, when running the node web server (that contains the SSR stuff (Server-Side Rendering)), an error is thrown:
ReferenceError: WebSocket is not defined
Example code:
// 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));
Please note, that the error doesn't occur on the browser version of the app (i.e. ng serve
won't throw the error), but only after compiling the SSR stuff and running the express web server. To reproduce the error, the builds have to be run first:
# 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'
I've also set up a reproduction repo.
Environment I'm using:
- Runtime: Angular 6
- RxJS version: 6.2.0, 6.2.1, 6.2.2 (tested all)
Edit 2018-08-02
I was able to address the problem more accurately. It seems, that this is also a webpack problem. Angular Universal creates a js bundle for running the angular app on the node server, but there is natively no websocket implementation on Node. Therefore, it has to be added manually as a dependency (npm package). I tried adding it to the js server bundle (const WebSocket = require('ws');
manually, which resolves the problem (i.e. ReferenceError disappears). However, when I add it to the TypeScript code that gets transcompiled into the js bundle later on, it won't work.
Further details
- The webpack loader
ts-loader
is used for compiling TypeScript => JavaScript - The websocket depedency was added to the package.json:
"ws": "^6.0.0"
- Attempting to reference the ws dependency by adding
const WebSocket = require('ws');
to the uncompiled TypeScript code won't resolve the issue. It would get compiled intovar WebSocket = __webpack_require__(333);
in the js output file, the dependency won't be able to be resolved. - Manually changing
var WebSocket = __webpack_require__(333);
=>const WebSocket = require('ws');
in the compiled js file would resolve the issue, but of course it's a hack.
So, the questions are:
- Can I "force" webpack to compile the dependency
const WebSocket = require('ws');
=>const WebSocket = require('ws');
(no changes)? - Or, would it might be a better solution, to register this dependency in the angular universal npm package and create a pull request?
回答1:
All it takes is this:
// set WebSocket on global object
(global as any).WebSocket = require('ws');
And of course, we need the ws
dependency as well in the package.json:
npm i ws -s
回答2:
The RxJS webSocket function can receive a WebSocket Constructor as an argument. This can be used to run webSocket in a non-browser/universal environment like this.
const WebSocketConstructor = WebSocket || require('ws')
const socket: WebSocketSubject<any> = webSocket({
url: 'wss://echo.websocket.org',
WebSocketCtor: WebSocketConstructor,
});
回答3:
In nodeJS apps with ES6 or similar, you must use this:
global.WebSocket = require('ws')
For example:
import { WebSocketSubject, webSocket } from 'rxjs/webSocket';
global.WebSocket = require('ws'); // <-- FIX ALL ERRORS
const subject = webSocket('ws://...');
No more errors.
My first answer!! Cheers
回答4:
I created a npm package using websockets. To provide compatibility for server side js and browser js i use this code.
(Note: it is typescript)
if (typeof global !== 'undefined') {
(global as any).WebSocket = require('ws');
}
Like you mentioned, the browser already has a WebSocket in its global object window
but node does not.
Since the browser does not have global
, this simple check works well.
Compiled code:
if (typeof global !== 'undefined') {
global.WebSocket = require('ws');
}
来源:https://stackoverflow.com/questions/51532159/referenceerror-websocket-is-not-defined-when-using-rxjs-websocketsubject-and