Sails 实时 WebSocket 客户端 `sails.io.js` 完全指南:从浏览器到 Node.js 的虚拟请求编程

📅 发布时间:2026/9/21 19:11:49
Sails 实时 WebSocket 客户端 `sails.io.js` 完全指南:从浏览器到 Node.js 的虚拟请求编程
Sails 实时 WebSocket 客户端sails.io.js完全指南从浏览器到 Node.js 的虚拟请求编程【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sailssails.io.js是 Sails 官方内置的 JavaScript 实时通信客户端它以极小的体积封装 Socket.IO为浏览器端与 Node.js 脚本提供了一套类 Ajax 的虚拟 HTTP 请求接口.get()、.post()、.put()、.delete()。阅读本篇指南你将掌握sails.io.js的引入方式、HTML 属性与io.sails两种配置路径、io.socket与SailsSocket的使用方法以及如何利用事件监听构建真实时的前端界面让你的 WebSocket 请求与现有 HTTP 路由完全复用。什么是sails.io.jsSocket.IO 之上的轻量封装Sails 的 socket 客户端sails.io.js是一个微型的浏览器库默认捆绑在每一个新建的 Sails 应用中位于assets/js/dependencies目录。它本质上是一个轻量封装器坐在 Socket.IO 客户端之上目标是让从你的 Sails 后端收发消息这件事变得尽可能简单。它的核心职责是提供一个熟悉的、类 Ajax 的接口用 WebSockets/Socket.IO 与你的 Sails 应用通信。具体来说它提供.get()、.post()、.put()、.delete()等方法让你在享受实时特性的同时依然复用应用里其他部分正在使用的后端路由。换言之在浏览器中执行io.socket.post(/user)与向同一路由发送 HTTP POST 请求在你的 Sails 应用内部会被以完全相同的方式路由——照常经过 routes、blueprints、policies、controllers 等环节。在实现层面文档 FAQ 给出了原理socket 客户端会发出名称被保留的 Socket.IO 消息Sails 收到后将其解释为虚拟请求并根据应用的路由和 blueprint 配置转发给相应的 policies/controllers 等详见 sails.io.js.md 的How does this work?小节。说明sails.io.js是独立于 Sails 核心框架的客户端库同时存在面向原生 iOS、Android、Windows Phone 的社区移植项目本文聚焦其 JavaScript 版本在浏览器与 Node.js 两种运行环境下的使用。快速上手浏览器端的基本用法在浏览器中使用sails.io.js的全部前提就是通过一个SCRIPT标签引入它。由于 Sails 会把该库添加到所有新应用的assets/js/dependencies文件夹可参考 assets 目录结构你只需写!-- 这将引入 Sails 应用默认捆绑的 sails.io.js 库。 捆绑版本同时内嵌了 Socket.io 客户端压缩代码。 脚本导入后经过一个事件循环周期会自动创建一个新的 eager socket 开始连接除非你配置它不这样做。 -- script typetext/javascript src/js/dependencies/sails.io.js/script随后在后续的内联或外部脚本中就可以把io.socket当作全局变量来使用了。例如script io.socket.get(/users/9, function (resData) { // resData {id:9, name: Timmy Mendez} }); /script这个默认的io.socket实例是SailsSocket类的一个全局实例其完整属性和方法请见 io.socket。在 Node.js 脚本中使用要在 Node.js 脚本中使用 Sails socket 客户端 SDK你需要同时安装并引入sails.io.js与socket.io-client两个库并用工厂函数将后者注入前者// 用 socket.io-client 模块初始化 sails.io.js 库 // 它会自动创建并连接一个新 socket 作为 io.socket // 除非你配置它不这样做。 var io require(sails.io.js)( require(socket.io-client) );在 Node.js 环境下自动创建的 socket 会挂载到初始化该库时使用的变量即上面的io的socket属性上见 io.sails 中autoConnect的说明。Node.js 端还独有一些配置能力例如initialConnectionHeaders详见后文。配置sails.io.js的两种方式本节聚焦最常见的运行环境——浏览器。在浏览器中配置 Sails socket 客户端有两种途径在script标签上使用 HTML 属性或以编程方式修改io.sails对象。方式一使用 HTML 属性配置配置 socket 客户端最常用的四个设置autoConnect、environment、headers、url的最简单方式是在 script 标签上挂一个或多个 HTML 属性script src/js/dependencies/sails.io.js autoConnectfalse environmentproduction headers{ x-csrf-token: % typeof _csrf ! undefined ? _csrf : % } /script这个示例做了三件事autoConnectfalse禁用 eager socket 的自动连接把连接时机交给你掌控environmentproduction将客户端环境强制设为 production从而关闭日志输出headers设置一个x-csrf-token头它会在每次 socket 请求中发送除非被单独覆盖示例中用 EJS 模板语法动态注入 CSRF token。注意headers这类复合值必须用一对单引号包裹。这是因为通过这种方式指定的复合值必须是JSON 编码的其键名和值字符串需要用双引号括起来同理值字符串内部的字符串再用单引号包裹。任何可以作为 HTML 属性的配置都可以改用data-前缀的形式提供如data-autoConnect、data-environment、data-headers、data-url以兼容那些对非标准 HTML 属性有问题的浏览器或者只是不喜欢非标准属性。如果标准 HTML 属性与data-前缀属性同时提供后者优先。注意若使用默认的 Grunt 资源管线它会自动注入 script 标签要采用这种配置方式你需要把sails.io.js从pipeline.js文件中移除改为显式添加一个script标签来引入它详见 tasks/pipeline.js 的相关说明。方式二编程式配置io.sails从 Sails v0.12.x 起只有最基本的配置项可以通过 HTML 属性设置。要配置其他选项你需要与io.sails对象交互。其实方式一只是方式二的一个便捷捷径——其原理是当sails.io.js通过script标签加载到页面后库会等待一个事件循环周期然后才自动连接 socket前提是io.sails.autoConnect处于启用状态。这给了你在 socket 开始连接之前设置io.sails上任意属性的机会。不过为了确保这些属性在连接前被读取你应该把设置属性的代码紧跟在引入sails.io.js的script标签之后script src/js/dependencies/sails.io.js/script script typetext/javascript io.sails.url https://myapp.com; /script !-- ...其他脚本... --正常情况下socket 客户端总是连接为其提供脚本的服务器。上面这个例子会让 eager自动连接socket 改为尝试与运行在https://myapp.com的 Sails 服务器建立跨域socket 连接。注意使用默认 Grunt 资源管线时同样建议把sails.io.js从pipeline.js中排除转而显式添加script标签。这样才能保证你的编程式配置例如设置io.sails.url在 eager socket 开始连接之前生效——因为内联script会紧跟在 socket 客户端之后立即执行。io.sails客户端全局配置对象io.sails对象是sails.io.js库及其创建的所有 socket 的全局配置之家详见 io.sails。其中大部分属性用作连接客户端 socket 时的设置或作为客户端库自身的顶层配置。它同时提供一个.connect()方法用于手动创建新的 socket 连接。.connect()方法如果io.sails.autoConnect为false或者你需要用sails.io.js创建多个 socket 连接可以通过io.sails.connect([url], [options])完成。两个参数都可选io.sails上的属性如url、transports等会被用作默认值var newSailsSocket io.sails.connect();io.sails.autoConnect当io.sails.autoConnect为true默认值时库会在加载后等待一个事件循环周期然后尝试创建一个新的SailsSocket并连接到io.sails.url指定的 URL。在浏览器中新 socket 会暴露为io.socket在 Node.js 脚本中新 socket 会挂载为初始化变量上的socket属性。io.sails.reconnection当io.sails.reconnection为true时socket 会在意外断开后自动且持续地尝试重连——所谓意外指不是由调用.disconnect()导致的断开。若为false默认值则不做任何自动重连尝试。默认值为false。io.sails.environment用io.sails.environment为sails.io.js设置环境影响输出到控制台的日志量。合法值为development完整日志和production最少日志。其他常用属性与默认值io.sails的其他属性会作为创建新 socket无论是 eager socket 还是通过io.sails.connect()创建的时的默认值。最常用的几个如下属性类型默认值说明url字符串io.sails.url的值socket 已连接或将要尝试连接的 URLtransports数组io.sails.transports的值socket 尝试连接时使用的传输方式。按顺序尝试且允许升级例如同时列出polling和websocket则先建立长轮询连接再由服务器尝试升级为 websocket 连接。该设置应与 Sails 应用中的sails.config.sockets.transports保持一致headers字典io.sails.headers的值该 socket 每次请求默认发送的请求头字典可通过.request()中的headers选项覆盖io.socket自动连接的默认 SailsSocket在浏览器中sails.io.js一加载就会创建一个全局的SailsSocket实例并在等待一个事件循环周期后以便允许你修改配置选项尝试连接服务器。与任何SailsSocket一样你可以在 socket 连接服务器之前就开始使用它的属性和方法——所有请求或事件绑定都会被排队待连接建立后自动重放详见 io.socket。例如改变io.socket连接的服务器script typetext/javascript src/js/dependencies/sails.io.js/script script typetext/javascript io.sails.url http://somesailsapp.com; /scriptio.socket受全局io.sails设置影响库在连接io.socket前会等待一个事件循环周期给你修改任何设置的机会。SailsSocket类属性与方法详解sails.io.js通过把底层的 Socket.IO 客户端包装成SailsSocket类的实例来工作详见 SailsSocket。默认情况下库几乎在加载后立即自动连接一个 socketio.socket这对 99% 的应用来说已经足够但对于某些高级用例包括自动化测试从同一个客户端实例例如同一个浏览器标签页连接额外 socket 会很有帮助这时SailsSocket类就派上了用场。常见属性这些属性在io.sails.connect的初始调用中设置创建SailsSocket后在 socket 连接期间无法更改headers除外。如果 socket 断开无论被动还是主动其属性可以修改直到 socket 重新连接——这意味着一个从某服务器断开的实例可以连接到另一台服务器而不丢失其事件绑定或排队中的请求。属性类型默认值说明url字符串io.sails.url的值socket 已连接或将要尝试连接的 URLtransports数组io.sails.transports的值socket 尝试连接的传输方式。按顺序尝试且允许升级应与sails.config.sockets.transports匹配headers字典io.sails.headers的值socket 连接后每次请求默认发送的请求头字典可通过.request()中的headers选项覆盖高级属性属性类型默认值说明query字符串io.sails.query的值与服务器初次连接时使用的查询字符串。在服务器代码中可通过控制器动作中的req.socket.handshake.query或 socket 生命周期回调中的handshake._query访问。注意关于sails.io.jsSDK 版本的信息会附加在你指定的查询字符串之后。query的一个常见用途是设置nosessiontrue表示 Sails 应用不应将该连接 socket 与会话关联initialConnectionHeaders字典io.sails.initialConnectionHeaders的值仅 Node.js 可用——浏览器中不可用。与服务器初次连接时发送的请求头字典区别于上面的headers后者用于初次连接之后每次 socket 请求。服务器代码中可通过req.socket.handshake.headers控制器动作或socket.handshake.headerssocket 生命周期回调访问。典型用途是发送cookie头以连接先前建立的 Sails 会话useCORSRouteToGetCookie布尔值或字符串io.sails.useCORSRouteToGetCookie的值仅在浏览器环境、且依赖默认 Sails 会话 会话 cookie 做认证时有意义。对于跨域 socket 连接用该属性指定一条路由让客户端先发送一个初始 JSONP 请求以获取 cookie从而建立正确的会话。路由应返回字符串_sailsIoJSConnect();以允许连接继续。若为true使用 Sails 服务器上的默认/__getcookie路由若为false则连接 socket 前不尝试联系远程服务器。注意该策略在默认阻止第三方 cookie 的某些浏览器含部分 Safari 版本上可能失败io.sails.*默认值速查表io.sails对象可为新客户端 socket 提供默认值。例如设置io.sails.url http://myapp.com:1234后除非在io.sails.connect()调用中显式提供url否则每个新 socket 都会连接该地址。以下是io.sails中各属性的默认值属性默认值url浏览器中为加载了sails.io.js脚本的页面 URLNode.js 中无默认值transports[websocket]headers{}queryinitialConnectionHeaders{}useCORSRouteToGetCookietrue高级方法除基本的通信与事件监听方法外每个SailsSocket实例包括io.socket还暴露若干与服务器连接相关的方法详见 SailsSocket methods。大多数方法甚至可以在 socket 连接服务器之前调用——像.get()、.request()这类请求方法在未连接时会排队直到连接建立后按顺序执行。.isConnected()判断实例当前是否已连接服务器已建立连接则返回true。io.socket.isConnected();.isConnecting()判断实例当前是否正在连接服务器正在尝试连接则返回true。io.socket.isConnecting();.mightBeAboutToAutoConnect()检测实例是否已加载但尚未完全配置、或尚未尝试自动连接。sails.io.js会等待一个事件循环周期再检查autoConnect是否启用并尝试连接——这让你有机会例如设置io.sails.url在建立连接之前配置实例。当sails.io.js已加载但对应的事件循环周期尚未过去时该方法返回true。io.socket.mightBeAboutToAutoConnect();.disconnect()将实例与服务器断开若已断开则抛出错误。io.socket.disconnect();.reconnect()在断开后被动断开或调用.disconnect()后重新连接服务器使用实例当前配置的属性连接。若已连接则抛出错误。当实例处于断开状态时可以修改其属性——因此一个从服务器 A 断开的实例可以不丢失事件绑定或排队请求地重连到服务器 B。io.socket.reconnect();.removeAllListeners()停止监听实例上的所有服务器相关事件包括connect和disconnect。io.socket.removeAllListeners();虚拟 HTTP 请求复用后端路由的 Ajax 式接口sails.io.js最大的价值在于socket 请求可以命中应用中任意一条路由。你不需要为 WebSocket 单独写一套接口。io.socket.get()与io.socket.delete()发送虚拟 GET / DELETE 请求详见 socket.get 与 socket.deleteio.socket.get(url, data, function (resData, jwres){ // ... });参数类型说明1url字符串目标 URL 路径如/checkout2_data_JSON可选可选请求数据。若提供会被URL 编码并追加到urlurl中已有的查询字符串参数会保留3_callback_函数可选可选回调服务器响应时调用回调收到两个参数resData服务器响应数据等同于jwres.body与jwresJSON WebSocket 响应对象含headers、body、statusCode。示例script io.socket.delete(/users/9, function (resData) { resData; // {id:9, name: Timmy Mendez, occupation: psychic} }); /scriptio.socket.post()、io.socket.put()与io.socket.patch()发送虚拟 POST / PUT / PATCH 请求详见 socket.postPUT 与 PATCH 的用法与其一致对应文档见 socket.put 与 socket.patchio.socket.post(url, data, function (resData, jwres){ // ... });参数类型说明1url字符串目标 URL 路径如/checkout2_data_JSON可选可选请求数据。若提供会被JSON 编码并作为虚拟 HTTP 请求体3_callback_函数可选可选回调服务器响应时调用示例script io.socket.post(/users, { name: Timmy Mendez }, function (resData, jwRes) { jwRes.statusCode; // 200 }); /scriptio.socket.request()更低级的控制io.socket.request()与上述方法非常相似但它对请求的方法、URL、头、参数提供了更底层的访问详见 socket.request。一个贴切的类比是io.socket.get之于io.socket.request就像 jQuery 的$.get之于$.ajax。io.socket.request(options, function (resData, jwres){ // ... // jwres.headers // jwres.statusCode // jwres.body resData // ... });选项类型说明method字符串HTTP 请求方法如GETurl字符串目标 URL 路径如/checkout_data_JSON可选若提供会被 JSON 编码并作为虚拟 HTTP 请求体_headers_字典可选若提供该字符串头字典将作为虚拟请求头发送完整示例io.socket.request({ method: get, url: /user/3/friends, data: { limit: 15 }, headers: { x-csrf-token: ji4brixbiub3 } }, function (resData, jwres) { if (jwres.error) { console.log(jwres.statusCode); // 例如 403 return; } console.log(jwres.statusCode); // 例如 200 });需要为所有传出请求设置自定义头请使用io.sails.headers。实时事件监听io.socket.on()与io.socket.off()订阅事件io.socket.on(eventName, handlerFn)用于监听 Sails 发来的指定事件名的 socket 事件收到匹配事件时触发回调详见 io.socket.onio.socket.on(eventName, function (msg) { // ... });参数类型说明1eventName字符串socket 事件名如recipe或welcome2handlerFn函数事件处理器服务器向该 socket 广播通知时被调用仅当传入通知与eventName匹配时才会触发事件处理器的唯一参数msg是来自 socket 通知的数据JSON。何时触发事件处理器当客户端收到与指定事件名如welcome匹配的 socket 通知时处理器被调用。这发生在服务器向该 socket 直接广播消息、或向该 socket 所属的房间广播消息时。要广播 socket 通知你需要使用blueprint API见 Blueprints 概念或编写一些服务器端代码例如在 action、helper 乃至命令行脚本中。典型实现途径有低级 socket 方法sails.sockets服务器向所有已连接 socket 群发消息sails.sockets.blast()或按唯一 ID 向特定 socket、或向整个房间广播消息sails.sockets.broadcast()。Resourceful PubSub 方法服务器广播与某条记录相关的消息多个 socket 可能订阅了它Model.publish()或作为 Create blueprint 动作的一部分广播仅在使用 blueprints 时相关。实战示例实时点餐系统设想你在为一个连锁餐厅构建点餐系统前端可以这样实时渲染新订单// 在你的前端代码中... // 此示例为简单起见使用 jQuery 和 Lodash你可以使用任意库或框架。 var ORDER_IN_LIST _.template(li>io.socket.on(connect, function onConnect(){ console.log(This socket is now connected to the Sails server.); });如果 socket 与服务器的连接被中断——例如服务器重启、或客户端出现网络问题——可以处理disconnect事件以显示错误提示甚至手动重连io.socket.on(disconnect, function onDisconnect(){ console.log(This socket lost connection to the Sails server); });socket 可以配置为自动重连但自 Sails v1 起socket 客户端默认禁用了该行为。实践中由于断连期间 UI 可能错过 socket 通知你几乎总是需要自行处理相关逻辑例如展示请检查你的网络连接错误提示。解绑事件io.socket.off()io.socket.off(eventIdentity, handlerFn)用于解绑指定的事件处理器是.on()的反操作详见 io.socket.off。io.socket.off(eventIdentity, handlerFn);参数类型说明1eventIdentity字符串与服务器发来消息关联的唯一事件标识如recipe2handlerFn函数要从指定事件上解绑的事件处理器函数注意此方法虽提供出来但大多数应用并不需要它而且使用时要格外小心io.socket.off()并不会阻止客户端 socket 继续接收任何服务器发来的消息它只是阻止指定的事件处理器被触发。通常理想的效果是根本阻止消息被发送——尤其当服务器发来的消息包含私密数据时这一点至关重要。socket 断开时这种阻止会自动发生但在一些不太常见的场景下需要在 socket 仍然连接时将其从房间中退订。例如一位管理员用户正在查看实时仪表盘时被封禁你的应用需要阻止他继续接收所有后续实时更新。此时不要使用本方法而应在服务器端代码中退订该 socket若房间是通过sails.sockets.join()加入的调用sails.sockets.leave()若房间是通过 resourceful PubSub 方法加入的酌情调用.unsubscribe()或.unwatch()。另外要使用.off()你需要把传给.on()的handlerFn存到变量中以便引用。其他注意事项socket 只在连接期间保持对房间的订阅——例如只要浏览器标签页打开——直到服务器端使用.unsubscribe()或.leave()手动退订为止。在监听 resourceful PubSub 调用和 blueprints 发来的 socket 消息时事件名始终等于调用方模型的 identity。例如模型名为 UserComment其 identity因而也是 socket 事件名是usercomment。socket 通知有时也被称为server-sent events服务器推送事件或 comet彗星消息。常见问题FAQ能和 XYZ 前端框架一起用吗可以。Sails socket 客户端可以与任何前端框架很好地配合无论是 Angular、React、Ember、Backbone、Knockout、jQuery 等。我必须用它吗不是必须。Sails socket 客户端在构建基于浏览器的 UI 的实时/聊天功能时非常有用但和assets/目录中的其余部分一样如果你在构建原生应用或一个完全没有用户界面的纯 API它可能用处不大。幸运的是与 Sails 中其他样板文件和文件夹一样socket 客户端完全可选——移除它只需删除assets/js/dependencies/sails.io.js。它是如何工作的底层上socket 客户端发出名称被保留的 Socket.IO 消息Sails 解释这些消息后会按照应用的路由和 blueprint 配置将它们路由到适当的 policies/controllers 等。如何告诉 Sails 应用不要将当前浏览器会话关联到 socket默认情况下socket 连接会通过初次 socket 握手时发送的cookie头关联到当前浏览器会话如果有。要关闭该行为请在 socket 连接前向其query属性添加nosessiontruescript src/js/dependencies/sails.io.js/script script typetext/javascriptio.sails.querynosessiontrue;/script能否绕过这个客户端直接使用 Socket.IO可以绕过应用中的请求解释器直接与 Socket.IO 通信但不推荐——这破坏了框架其他部分遵循的约定优于配置理念。sails.io.js是非侵入式的它包装原生 Socket.IO 客户端并暴露一个更高级的 API利用 Sails 中的虚拟请求解释器发送模拟 HTTP 请求。这让后端代码更可复用降低了 WebSockets/Socket.IO 初学者的上手门槛也让应用更容易推理。在极少数情况下例如与直接使用 Socket.IO 的既有/遗留前端兼容绕过请求解释器是必需的。如果你处于这种情况可以使用 Socket.IO 客户端 SDK然后在后端用sails.io访问原始 Socket.IO 实例。只有在具备丰富的 Socket.IO 直接使用经验、并且先研究了socketshook 内部实现的前提下才建议走这条路——特别是其中的 admin bus 实现它是构建在 sailshq/socket.io-redis 之上的 Redis 集成为 Sails 的多服务器加入/离开房间能力提供支持。结语与 Sails 服务端配置的呼应io.sails与 Sails 服务端存在一组需要保持一致的配置。最典型的是transports客户端io.sails.transports默认[websocket]应匹配 Sails 应用中的sails.config.sockets.transports服务端配置详见 sails.config.sockets以保证连接协商过程顺畅。理解客户端与服务端的对应关系是让实时应用在生产环境稳定运行的最后一环先用 HTML 属性或io.sails完成最基本的连接配置再用io.socket.*方法复用既有路由最后用io.socket.on()接收服务器推送即可搭建出一套完整的、与 HTTP 后端完全同构的实时通信层。【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考