[开源演示][001期]只用CSS能实现聊天室?只用CSS也能发请求!

By | 2023-08-10

一、简介

  • 闲来无事,刷刷Github,这样一句话一下吸引到了我。
  • “一个真正可怕的异步web聊天,前端不使用任何JS。”
  • 看看效果图
  • 1.gif
  • 看效果还挺不错的,好像真的能聊天!
  • 虽然这好像并没有什么意义…但最使我好奇的几个点是:没有js,那么这个聊天室是怎么实现的呢?消息如何传递?为啥不让键盘直接输入文字呢?(神秘度=3;)

二、浅看

  • 打开源码浅浅预览一下
  • 2.png
  • 可以看到目录结构十分简短,并且没有任何html类型文件和js类型文件,项目主体语言是Ruby,并且使用了Redis作为数据库。
  • 那么我们现在可以猜测:逻辑是写在Ruby里面的、聊天信息是通过Redis传递的。(神秘度—;)
  • 具体怎么搞,一会再揭晓。

三、本地复现

  • 根据README.md的提示,我们首先需要准备好Ruby和Redis环境。(环境安装这里就不多赘述了,大概就是下载安装包或者绿色版压缩包之后安装到本地、配置环境变量、运行环境,当然可以使用虚拟机或Docker进行安装)
  • 我是在Windows上进行复现的,所以需要下载RubyInstaller和Redis-x64-5.0.14.1.zip。
  • 安装好之后,启动完环境。我们的目光就转向了源码,Ruby的包管理工具叫做Gem(可能很多同学不了解Ruby所以在这里说明一下),在源码目录中有一个叫做Gemfile的文件,这个文件就是用来管理项目所需要的Gem包的。在Gemfile文件中我们可以看见项目依赖于Rack、Puma、Redis、Faker等包,你可能对他们不熟悉,但不要紧,这些和原理无关,我们只需要知道这些包是用来干什么的就行了。Rack和Puma能够实现http服务、Redis是数据库、Faker是用来生成假数据的。我们直接控制台cd到项目目录,然后我们上来就是一条指令:bundle install,这条指令会根据Gemfile文件中的内容自动安装所需要的包(网速你懂的,如果批量安装指令一直失败可以使用gem install xxx的指令逐个安装),安装完之后我们就可以运行项目了。
  • 最后执行bundle exec puma使用Puma启动项目,然后打开浏览器访问http://localhost:9292,就可以看到效果了。
  • 3.png
  • 演示视频:https://www.bilibili.com/video/BV17e4y1T7gM/

四、原理解析

  • 在我们打开Web页面的时候,一定会发现一个现象!就是这几个界面加载的圈圈一直在转,好像从来没有停止加载一样。而且,明明在文件中没有任何html文件,也渲染出来了界面(这其实并不神奇)。
  • 首先来看一下没有html文件就能渲染出来界面的问题,这个问题其实很简单。我们平时访问的页面被称为超文本,超文本从服务器发送到用户页面不一定是html文件,可以是其他类型文件,也可以是写死的包含一系列标签的字符串(这种用法在物联网芯片编程中很常见),因为本质上浏览器向服务地址发送http请求得到的响应数据只要是浏览器能读懂的响应头、响应行、响应体就可以顺利的被渲染。我们要做到让浏览器只要能识别出来就行了,所以这里的界面其实就是一个写死的标签字符串拼接起来的。
  • 咱们去项目中唯一一个rb文件查找页面中的标志性信息
  • 4.png
  • 一目了然!实际上这个开源项目中就是搭建了一个http服务器,并且在/请求路径下建立响应处理和响应内容(使用的技术是上文中提到的Rack+Puma),并且响应内容是我们看见的界面,就这么简单。
  • 那项目是如何实现通信的呢?在哪触发了方法?值得我们注意的点在于输入字母的时候,我们是通过点击屏幕上的字母按钮输入文字,秘密其实就在这个按钮里面。
  • 5.png
  • 当我们点击按钮的时候,竟然会出现网络请求!并且是路径有规律的网络请求!界面也同时会跟着改变!
  • 让我们先将现在的信息整理一下:页面在不停加载、只有CSS文件、点击按钮会触发网络请求
  • CSS怎么发请求?这是一个很有意思的问题,很难想,但是在平时开发中却十分常用!十分常见!常用到有图片的地方,每次换图片都得调用一次网络请求。这么说你还没懂的话,我可就直说了!
  • 可以直接通过设置CSS的background-image属性来发起网络请求,这个属性的值可以是一个图片的url,也可以是一个base64编码的图片字符串。当这个地址是一个可请求路径的时候,浏览器就会向对应地址发起请求获得背景图片!并且!CSS中的伪类选择器active能够知道我们点击了按钮!然后转换样式!(神秘度—;)。但是浏览器不会重复加载一个图像两次的,想想我们在做网页验证码的时候是怎么做的?加时间戳?对!就是这样!但是这里我们不能调用js内置时间对象来产生时间戳了,要整点不一样的了!
  • 既然发请求的问题解决了(虽然方案可行,但是目前来说每个按钮只能用一次,因为浏览器不会重复请求图片),我们先来看看如何接收数据。
  • 传统的实现动态网页的方式是使用js发请求,拿到返回值之后使用js依据返回值去操作DOM元素,但是现在这个项目中作者标题写的只能用CSS来实现聊天室(虽然真的有点标题党,因为后端服务器里面也有很多逻辑),但还是坚决不能出现js文件的!那这个项目的页面是怎么拿数据更新界面的呢?
  • 要实现改变界面还不用CSS,怎么办?恐怖如斯!但是咱们再来看看目前整理的信息都有啥(当前神秘度=1)
  • 目前信息:页面在不停加载、只有CSS文件(√)、点击按钮会触发网络请求(√)
  • 现在发送网络请求的问题解决了,第二和第三个信息就用完了,还剩下俩信息了,我们先来看看第一个信息:页面在不停加载。
  • 有趣的事情来了!我们在浏览器中打开开发者工具,然后刷新页面,然后在Network中找到我们的请求,然后点击Preview,然后我们就会发现…
  • 6.png
  • 注意!请求尚未完成!而且离谱的是页面打开多久,这个请求就请求多久。
  • 想改变页面,除了我们使用代码操作DOM。还有一种经常使用,但是我们似乎从来没有仔细研究过的方法,那就是——加载页面!
  • 这里开源软件的作者使用了一种可以说是十分极客的手段,让页面永远无法完成加载,并不断通过加载的数据流向页面中添加东西(注意:只能添加),听起来挺离谱的,但这样确实可以!
  • 通过在请求头中加入Transfer-Encoding: chunked,可以让浏览器在完成加载之前开始呈现页面。(神秘度—;)
  • 至此,一切秘密都解开了!(当前神秘度=0)
  • 现在是填坑时间了。可能你很好奇,明明只能添加东西不能删除东西(如果直接都想通了就当我没说),不能删掉旧的按钮或者删除旧的样式,那如何实现每次点击按钮的时候发送不同的请求呢?其实只要细心就会发现一件很绝的事情,div标签会越点越多!
  • 7.png
  • 每点击一次按钮,就会多一整套按键,并且老一套按钮会被完全隐藏。这是能够通过添加页面内容实现的,因为存在着可以隐藏控件的属性,并且样式是可以覆盖的。
  • 至于聊天的功能,在这里其实无足轻重。请求和响应都是使用Ruby的Web框架处理的,我们知道聊天的消息是存在Redis的,聊天室中的所有人存在一个set类型变量中,用来管理在线状态。消息列表放在一个List类型中,用来共享消息。我们使用Redis可视化工具查看一下Redis中的数据就很容易看懂了。
  • 8.png

五、难点

  • 9.png
  • 10.png

六、相关代码

七、总结

  • 这个项目虽小,但是十分精悍,里面使用的技巧和思路都是十分值得学习的,希望大家能够喜欢。
  • 感谢您能耐心看完这篇文章,如果有什么问题,欢迎在评论区留言,我会尽快回复。
  • 如果您觉得写的还不错,欢迎关注、点赞、分享、订阅、Star,您的点赞是我写作的动力。