Web、PC客户端、手机客户端、后台服务端,全栈JS开发技术总结

410 阅读6分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

简介

这是一个真实在线项目的技术总结,根据项目要求与开发资源,最终选择了全栈JS的开发,以JS作为主要开发语言,实现了web、pc客户端、手机客户端、后台服务端。虽然用js在在各端实现简单的Hello world很容易,但是在实际项目中应用时,为了满足各种功能和要求,依然碰到很多棘手的问题,本文主要对这些问题及方案做一些记录与梳理。

项目简介:
创建一个工具,让各种PC、手机、电脑等设备轻松直连互通,相互之间可以安全、快速的传输文件、文字、控制命令等(具体功能参考项目www.ppzhilian.com)。

link.png

技术选型

选择的技术方案需要满足以下要求:
1、支持多平台(windows、mac、linux、android、ios)
2、支持多种设备(PC、手机、平板、服务器)
3、跨网络(局域网、互联网)
4、代码尽量复用,UI尽量保持一致

初步方案就是开发一个web网站,基于webRTC实现底层点到点直连,然后再设计上层具体功能。
但是,此方案存在一些限制:
1、用webRTC传输时,所在网站必须在可见状态下,否则浏览器会自动停止隐藏页面的运行,甚至直接关闭页面内容。
2、webRTC进行大数据传输时,对cpu等资源的占用很高,整体的传输效率受限于设备的能力
3、无法主动读取本地文件系统,无法实现远程控制

所以针对高速传输大文件、持久化文件分享、远程控制等功能,需要专门的客户端补充解决上面的问题。由于涉及的平台比较多,需要选择跨平台的技术方案,来减少开发和维护的工作量。比较js、go、flutter等各种不同的跨平台方案与框架,综合考虑后,js更加合适。一来相关方案发展更久,更成熟;二来和网站版完全兼容,大部分代码可以共用

所以,最终技术方案是:
1、开发可运行在浏览器内的web app
2、基于electron,复用1中的web app,开发 windows、mac、linux客户端
3、基于capacitorjs,复用1中的web app,开发android、ios的客户端
4、基于nodejs,复用1中部分函数,开发信号服务器(辅助设备之间相互发现与连接)

然后就是选择合适的技术框架,用以提高开发效率。js框架很多,主流react、vue、angular等,还有很多整合型的开发框架,最终选择了 quasar framework,因为这个框架本身就是多平台的开发框架,上述1、2、3都可以完美支持,而且社区很活跃,更新特别快。

Web app技术要点

  1. 通过webRTC实现内网穿透,点到点直连
  2. 通过websocket和信号服务器的中转实现设备双方信令交互,辅助RTCPeerConnection的建立和维护
  3. 通过IndexedDB实现数据持久化和缓存
  4. 通过canvas实现可视化数据

webRTC

  1. 不同浏览器里,webRTC的api有所差别,可以选用一些webRTC的辅助库,比如simple-peer
  2. webRTC中datachannel的最大数据包大小有限制,不同浏览器(不同版本)允许的大小不一致,注意不能超过最大值,否则连接中断。一般不能大于128k字节
  3. webRTC的数据传输模式设置,注意maxPacketLifeTime与maxRetransmits只能一个生效,ordered为true时,maxPacketLifeTime与maxRetransmits可以不设置,如果设置必须为null
var tcpLike           = { ordered: true,  maxPacketLifeTime: null, maxRetransmits: null };
var ??                = { ordered: true,  maxPacketLifeTime: null, maxRetransmits: 0    };
                      = { ordered: true,  maxPacketLifeTime:    0, maxRetransmits: null };
var signalPerSecond   = { ordered: true,  maxPacketLifeTime: 1000, maxRetransmits: null };
var ??                = { ordered: true,  maxPacketLifeTime: null, maxRetransmits: 5    };
var ??                = { ordered: true,  maxPacketLifeTime: 1000, maxRetransmits: null };
var imageTransfer     = { ordered: false, maxPacketLifeTime: null, maxRetransmits: null };
var udpLike           = { ordered: false, maxPacketLifeTime: null, maxRetransmits: 0    };
                      = { ordered: false, maxPacketLifeTime:    0, maxRetransmits: null };
var ??                = { ordered: false, maxPacketLifeTime: null, maxRetransmits: 5    };
var ??                = { ordered: false, maxPacketLifeTime: 1000, maxRetransmits: null };
  1. 传输mediaStream时,根据特定需要选择不同的编码,比如h264,vp8, vp9,av1等,不同编码清晰度、性能、带宽要求等各不相同,如果清晰度优先,推荐av1。通过在建立连接时调整m=video后面编码号的顺序来指定优先级。

indexedDB

  1. 存储结构化的数据,使用前需要好好设计index,对数据操作效率影响巨大。
  2. indexedDB在不同浏览器和设备上,大小限制各不相同,如果存储大文件需要关注,做可用存储大小的判断
  3. 大文件写入时,写入速度较快
  4. indexedDB支持input file读取的File对象存储

桌面共享

  1. 手机浏览器不支持
  2. 可以通过关闭相应的mediaStream来停止分享

Electron 技术要点

相关教程已经很多,不再展开,整理一些项目中使用的注意点

  1. 通过exposeInMainWorld,可以把main进程内的函数暴露给renderer进程使用
  2. 通过webContents.send与ipcMain.once构建invoke函数,可以把renderer进程的函数暴露给main进程使用
  3. 尽量使用异步接口,否则可能会造成renderer进程代码停止运行,尤其是调用本地dialog时
  4. main进程向renderer进程传输二进制数据时,blob效率最高,arrayBuffer次之,但是如果需要在renderer中处理数据时,需要用FileRender转换,所以需要总体考虑用哪种类型传输。
  5. 引入外部可执行文件时,注意文件位置,development下与production下查找方式可能不一样

capacitorjs 技术要点

capacitorjs比cordova用起来更方便,增加本地插件也比较容易,而且还可以复用cordova的插件。

  1. 通过本地插件的暴露接口,可以允许webview中js直接调用
  2. 通过js中插件的addListener构造invoke函数,可以允许插件直接调用js函数
  3. 插件内尽量使用异步接口,因为插件是单线程的
  4. 把js内二进制文件直接写入本地时效率很低,如果是大文件,可以通过在本地内创建http server,然后js通过upload的方式进行写入,效率会大幅提高。
  5. 手机webview不支持桌面共享,可以在本地抓取桌面,然后本地与webview建立webRTC,把mediastream传递到webview中