使用 socket.io 的时候,打包后台代码碰到的问题

前言

一般情况下,我们只会针对前端代码,用 webpack 或者其他流行的打包工具进行打包。

因为对于前端而言,打包有好些不言而喻的好处,比如:

  1. 代码开发的时候更模块化
  2. 在生产环境下,减少 http 请求,使页面加载更快
  3. 在 babel 等工具的协助下,让我们可以在老版本的浏览器上运行最新的 js 特性
  4. 可以选择性的使用 typescript 进行开发,给前端代码加上类型限制
  5. 。。。

由我上面列的几条可以看到,对于目前阶段,前端项目打包,已经是主流的选择了。

但是你有没有碰到后端也要打包的呢?

其实对于 Java 或者 c++ 这种静态类型的语言而言,这应该是一个比较正常的需求吧,毕竟这些语言写的代码,本身就需要编译了以后才能运行。

但是对于 nodejs 而言,这种打包需求反而不是太常见。

毕竟,nodejs 本身就是 JavaScript 的 runtime,直接就是用来运行 JavaScript 代码的。

几种对 nodejs 后台代码打包的方案

但是有时候,偏偏这种不寻常的需求就是不期而至。

不过幸运的是,这种需求虽然不同寻常,但是也并非没有解决之道。

简单的搜索一下,发现主要有以下几种方式:

  1. ncc:https://github.com/vercel/ncc
  2. pkg: https://github.com/vercel/pkg
  3. bytenode:https://github.com/OsamaAbbas/bytenode

最后一个有点类似于 c++、Java 的编译,将 js 代码编译成字节码;pkg 也挺有意思的,将代码和 nodejs 打包成一个可执行的二进制文件;ncc 有点类似于 webpack 打包前端代码。

本文主要想研究的并非怎么打包 nodejs 代码,因此,在这里不做详细的赘述。

进入主题

说了这么就,终于引出来了本文想要研究的一个主题 —— ncc 打包socket.io 的时候,还会需要依赖才能运行。

这其实是很奇怪的一件事情,因为正常情况下,ncc打包以后,其实是将所有的依赖都打到 bundle 里面去了。

别问我是怎么知道的,🧐因为我在好几个 nodejs 项目中测试过。

而 ncc 打包以后的代码,居然需要 socket.io-client 模块才能运行。

我终于决定搞明白为什么,而且有了这篇文章,也是在我碰到了多次这个问题以后的后续了。

因为这个问题着实太令人困扰了,为啥偏偏是 socket.io-client 模块呢?

为啥后台用 socket.io 模块,却需要我 node_modules 里有 socket.io-client 才能运行呢?

带着这样的困惑,我开始探索起来了。

循序渐进

有人说 nodejs 后台代码太难调试,我极度不认同这个说法,这只是一种认知的偏见。

难用的是工具,并非是语言本身。

就像我刚开始学习 c 语言的时候,学着用 gdb 调试程序,那感觉,简直不要太酸爽。

虽然现在看来是我喜欢的方式,但是对于初学者来说,特别是对于一枚一直习惯了使用 Windows 系统的各种 gui 程序的用户来说,实在是太痛苦了。

后面写 js,发现有 Chrome devtool 这种逆天的工具,当时的惊喜感,丝毫不亚于哥伦布发现新大陆。

简直不要太秀了,有了这个工具的辅助,前端代码还有啥秘密可言,简直不要太 easy!

后来有个 vscode,调试 nodejs 代码,也开始变得极度舒适了。

所以想找到问题出在哪儿,就只能借助 vscode,来对打包后的代码进行 debug 了。

执行打包后的代码的时候,你会发现提示找不到这个文件,如下图所示。
在这里插入图片描述
当然这是我在 Mac 系统下测试显示的情况,Windows 下稍有不同,因为这个地方,Windows 的代码没有对路径进行转换处理,而 Mac 下做了,因此在 Windows 下,我更能发现,这个文件是处于 socket.io-client 模块中的。

那么就让我门找到这段代码,看一下,他究竟干了啥。

寻根究底

为了方便查看,直接在 github 找到这段代码的源码

在这里插入图片描述

可以看到,他需要从 socliet.io-client 模块中读取 socket.io.js 文件,然后赋给 clientSource 这个变量。

而且,在 opts.serveClient 不等于 false 的时候,才会执行这段逻辑。

在这里插入图片描述

所以我们现在就明白了,我们该如何操作来规避这个错误了。

创建 io 的时候,传入配置参数,将 serveClient 设置为 false,这个错误就可以避免了。
在这里插入图片描述

但是让人不解的是,为啥,socket.io 的源码里面,会有这么段代码呢?他又是干啥用的呢?

稍微分析下,可知,我们应该从 clientSource 这个变量入手。

全局搜索一下,很快就能发现,原来是在 serve 这个方法里提供某种路由服务。
在这里插入图片描述

看注释,结合 socket.io 的特点,不难发现,可以发送 /socket.io/socket.io.js 请求尝试一下。

serveClient 配置为 true,再次重启一下服务。
在这里插入图片描述

果不其然,谜底解开了:
在这里插入图片描述
这就是提供了 socket.io.js 文件在服务器中的路由位置。

开拓进取

我不清楚这段代码的意义在哪儿,我姑且揣测一下,可能是为了让前端代码,直接可以从后端服务器上拿到该文件吧。

为了验证我的揣测,我于是掏出了尘封已久的 Chrome,用颤抖的手,在地址栏键入了一行 url:https://google.com
在这里插入图片描述
看到了久违的画面,我的手愈发的颤抖了,于是按耐住激动不安的心以及颤抖不已的手👋,郑重的键入了 socket.io serverClient 关键字,之后顺手熟练的按下了回车键。

这过程,彷佛已经在我脑海里演练过无数遍,就如同刚才那一刹那,按下回车的手丝毫没有犹豫,内心更是没有一丝丝的波动。
在这里插入图片描述

果不其然,在第二条结果里面,我找到了我想要的答案
在这里插入图片描述

虽然写的很隐秘,甚至作为一个外行人压根看不出来其用意何在。

但是不知为何,我就是那么的懂了,不知是作为一枚根正苗红的前端的直觉,还是作为一枚“伪全栈”的直觉。

深入探究

虽然我弄明白了,但是为了彻底的好人做到底,我又多进行了几步操作,想看看又没有人有我一样的困扰。

果不其然,确实有好些人碰到同样的问题。
在这里插入图片描述

有人在 ncc repository 里提了 issue,官方貌似也发现了问题所在,做出了对应的 fix bug 的操作,也就是下面 👇 这波结果的缘由了。
在这里插入图片描述

这也是让我极其困惑的一个地方,同样的一套代码,在 Mac 系统下,就会打包出上图的结果,而在 Windows 系统下,并没有产生出这样的结果。

所以,总结一下,最好的方式,就是将 serverClient 参数设置为 false,那么就皆大欢喜了,即使不带上那两个文件,也不会出错了。

而且为了进一步验证我的想法,我再次进行搜索
在这里插入图片描述

发现在 stackoverflow 上,大家也讨论的很激烈。

有很多年以前的讨论,也有近期的讨论。

看来还是有很多人没搞明白这茬的。

综合看来,这确实是由于 socket.io 模块,引起的一些误会,从而导致的一系列连锁反应。

看来写文档,写代码,确实需要多站在用户的角度去思考下问题才好。

不然,也许很多在我们看来顺其自然的事情,在用户看来确是奇技淫巧了,甚至于有可能会弄巧成拙了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章