国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

如何在零JS代碼情況下實現一個實時聊天功能?

shiweifu / 3556人閱讀

引言

前段時間在 github 上看到了一個很“trick”的項目:用純 CSS(即不使用 JavaScript)實現一個聊天應用 —— css-only-chat。即下圖所示效果。

在我們的印象里,實現一個簡單的聊天應用(消息發送與多頁面同步)并不困難 —— 這是在我們有 JavaScript 的幫助下。而如果讓你只能使用 CSS,不能有前端的 JavaScript 代碼,那你能夠實現么?

原版是用 Ruby 寫的后端。可能大家對 Ruby 不太了解,所以我按照原作者思路,用 NodeJS 實現了一版 css-only-chat-node,對大家來說可能會更易讀些。
1. 我們要解決什么問題

首先強調一下,服務端的代碼肯定還是需要寫的,而且這部分顯然不能是 CSS。所以這里的“純 CSS”主要指在瀏覽器端只使用 CSS。

回憶一下,如果使用 JavaScript 來實現上圖中展示的聊天功能,有哪些問題需要處理呢?

首先,需要添加按鈕的click事件監聽,包括字符按鈕的點擊與發送按鈕的點擊;

其次,點擊相應按鈕后,要將信息通過 Ajax 的方式發送到后端服務;

再者,要實現實時的消息展示,一般會建立一個 WebSocket 連接;

最后,對于后端同步來的消息,我們會在瀏覽器端操作 DOM API 來改變 DOM 內容,展示消息記錄。

涉及到 JavaScript 的操作主要就是上面四個了。但是,現在我們只能使用 CSS,那對于上面這幾個操作,可以用什么方式實現呢?

2. Trick Time 2.1. 解決“點擊監聽”的問題

使用 JavaScript 的話一行代碼可以搞定:

document.getElementById("btn").addEventListener("click", function () {
    // ……
});

使用 CSS 的話,其實有個偽類可以幫我們,即:active。它可以選擇激活的元素,而當我們點擊某個元素時,它就會處于激活狀態。

所以,對于上面動圖中的26個字母(再加上 send 按鈕),可以分配不同的classname,然后設置偽類選擇器,這樣就可以在點擊該字母對應的按鈕時觸發命中某個 CSS 規則。例如可以對字符“a”設置如下規則用于“捕獲”點擊:

.btn_a:active {
    /* …… */ 
}
2.2. 發送請求

如果有 JavaScript 的幫助,發送請求只需要用個 XHR 即可,很方便。而對于 CSS,如果要想發一個請求的話有什么辦法么?

可以使用background-image屬性,將它指定為某個 URL,這樣前端就會向服務器發起一個背景圖片的請求。之所以可以使用background-image屬性還因為:瀏覽器只有在該 CSS 選擇器規則被實際應用到 DOM 元素后才會實際發起background-image的請求。例如下面這個規則:

.btn_a:active {
    background-image: url("/keys/a");
}

只有在字符“a”被點擊后,瀏覽器才會向服務器請求/keys/a這張“圖片”。而在服務器端,通過判斷 URL 可以知道前端點擊了哪個字符。例如,對于按鈕“b”會有如下規則:

.btn_b:active {
    background-image: url("/keys/b");
}

這樣就相當于實現了在 URL(/keys/a/keys/b) 中“傳參”。

2.3. 實時消息展示

實時的消息展示,核心會用到一種叫“服務器推”的技術。其中比較常見方式有:

使用 JavaScript 來和服務端建立 WebSocket 連接

使用 JavaScript 創建定時器,定時發送請求輪詢

使用 JavaScript 和服務端配合來實現長輪詢

但這些方法都無法規避 JavaScript,顯然不符合咱們的要求。其實還有一種方式,我在《各類“服務器推”技術原理與實例》中也有提到,那就是基于 iframe 的長連接流(stream)模式。

這里我們主要是借鑒了“長連接流”這種模式。讓我們的頁面永遠處于一個未加載完成的狀態。但是,由于請求頭中包含Transfer-Encoding: chunked,它會告訴瀏覽器,雖然頁面沒有返回結束,但你可以開始渲染頁面了。正是由于該請求的響應永遠不會結束,所以我們可以不斷向其中寫入新的內容,來更新頁面展示。

實現起來也非常簡單。http.ServerResponse類本身就是繼承自Stream的,所以只要在需要更新頁面內容時調用.write()方法即可。例如下面這段代碼,可以每隔2s在頁面上動態添加 "hello" 字符串而不需要任何瀏覽器端的配合(也就不需要寫 JavaScript 代碼了):

const http = require("http");
http.createServer((req, res) => {
    res.setHeader("connection", "keep-alive");
    res.setHeader("content-type", "text/html; charset=utf-8");
    res.statusCode = 200;
    res.write("I will update by myself");

    setInterval(() => res.write("
hello"), 2000); }).listen(8085);

2.4. 改變頁面信息

在上一節我們已經可以通過 Stream 的方式,不借助 JavaScript 即可動態改變頁面內容了。但是如果你細心會發現,這種方式只能不斷“append”內容。而在我們的例子中,看起來更像是能夠動態改變某個 DOM 中的文本,例如隨著點擊不同按鈕,“Current Message”后面的文本會不斷變化。

這里其實也有個很“trick”的方式。下圖這個部分(我們姑且叫它 ChatPanel 吧)

其實我們每次調用res.write()時都會返回一個全新的 ChatPanel 的 HTML 片段。于此同時,還會附帶一個