協作:誰在這裡、即時游標與編輯鎖定
顯示正在看同一份文件或工單的每個人的頭像、分享游標與焦點,並廣播已儲存的變更,誰都不會蓋掉別人的修改。
兩個人在不知情的情況下編輯同一筆資料,總有一個人的心血會被蓋掉。顯示還有誰在看同一份文件、工單或設計稿,以及他們在哪裡,就能避免這件事,也讓產品有「一起做事」的感覺。專案管理工具、CRM、客服系統、白板和表單建立工具都用得上。
使用者得到什麼
- 正在看同一頁的每個人的頭像,有人來去時跟著更新。
- 即時游標,或在別人正在編輯的欄位上標示出來。
- 清楚的「Ann 正在編輯這張工單」鎖定提示,而不是互相衝突的儲存。
- 別人的修改直接出現在畫面上,不必重新載入。
用到的功能
- Presence 頻道(
presence-),每份文件一個,成員名單在每個節點上都保持正確。 - 用戶端事件(
client-*),在瀏覽器之間傳送游標位置與焦點。 - 伺服器事件,在你的伺服器儲存修改之後才發布。
- 大型 presence 頻道(
presence-large-),用在同時可能有超過 100 人觀看的頁面。
步驟 1:顯示誰在這裡
用檢查文件存取權的同一套方式授權 presence-doc-<id>,並以 user_info 回傳名字與頭像(路由寫法請看聊天與輸入中提示)。接著:
const doc = pusher.subscribe('presence-doc-' + docId);
doc.bind('pusher:subscription_succeeded', (members) => avatars.render(members));
doc.bind('pusher:member_added', () => avatars.render(doc.members));
doc.bind('pusher:member_removed', (m) => {
avatars.render(doc.members);
cursors.remove(m.id);
});同一位使用者開了兩個分頁只算一次:第一個連線加入時新增,最後一個連線關閉時移除。
步驟 2:分享游標與焦點
在應用程式設定裡開啟 允許用戶端事件。位置每秒最多送幾次,而且只在有變化時才送:
let pending = null;
document.addEventListener('pointermove', (e) => {
pending = { x: e.pageX / document.body.scrollWidth, y: e.pageY };
});
setInterval(() => {
if (!pending) return;
doc.trigger('client-cursor', pending);
pending = null;
}, 200);
doc.bind('client-cursor', (pos, meta) => cursors.move(meta.user_id, pos));
// Field locks: who is editing what
field.addEventListener('focus', () => doc.trigger('client-focus', { field: field.name }));
doc.bind('client-focus', ({ field }, meta) => locks.show(field, doc.members.get(meta.user_id)));在 presence 頻道上,用戶端事件處理函式的第二個參數帶有發送者的 user_id。
步驟 3:廣播已儲存的修改
有人儲存時,先存下修改,再發布給其他人:
app.patch('/docs/:id', requireLogin, async (req, res) => {
const change = await db.docs.apply(req.params.id, req.user.id, req.body.patch);
await pusher.trigger('presence-doc-' + req.params.id, 'changed', { version: change.version, patch: req.body.patch, by: req.user.id }, { socket_id: req.body.socket_id });
res.json(change);
});其他觀看者套用這份修改;如果自己的版本落後,就重新載入資料。
超過 100 位觀看者
Presence 頻道最多 100 名成員,因為每個人都會收到完整名單。很多人同時在看的頁面,例如全公司公告或直播活動,請改用 presence-large-doc-<id> 搭配 @hibira-io/large-presence:最多可容納 100,000 名成員,傳送的是人數與成員樣本,而不是每一次加入。那裡不允許用戶端事件,所以游標只在較小的房間裡分享。
小提醒
- 每個用戶端事件都算一則訊息。十位編輯者每秒各送五次游標,就是每秒 50 則訊息,所以有變化才送,分頁隱藏時放慢。
- 資料的正本留在你的伺服器上。用戶端事件是用來傳游標、焦點這類提示的;它們不會被儲存,也不會回傳給發送者。
- 要做完整的協作文字編輯,用同樣的方式透過伺服器傳送 Yjs 或 Automerge 這類 CRDT 函式庫的操作。