FastComments.com

撰寫擴充套件

開發擴充功能

背景

FastComments 提供透過我們稱為擴充功能的腳本來擴充我們的核心功能的能力。

An Extension 可以向留言元件新增額外的標記、事件監聽器,並執行任意程式碼。

在這裡你會找到我們在生產環境中使用的擴充功能範例,以及如何撰寫擴充功能的文件。

擴充套件生命週期 Internal Link

每個擴充功能的腳本會在評論元件開始抓取第一批評論並渲染 UI 之前被抓取並執行。

在初始載入時,下列資料會被標記到擴充功能物件上:

  • config - 一個指向 config 物件的參考。
  • translations - 一個指向 translations 物件的參考。
  • commentsById - 一個以 id 為索引的所有評論的參考。
  • root - 一個指向根 DOM 節點的參考。

擴充功能應覆寫所需的函式,評論元件會在適當的時機呼叫它們。

定義擴充套件 Internal Link

最小的擴充功能範例為:

一個簡單的擴充功能
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6})();
7

為了本範例,將此儲存為 my-extension.js,並讓它可透過 https://example.com/my-extension.min.js 存取。

這個擴充功能本身不會執行任何動作,只有在載入時會取回由核心留言函式庫所建立的擴充物件。

此 Extension 物件為單例,且不會與其他擴充功能共用。

接下來,要載入我們的擴充功能,必須告知留言小工具。例如:

Using a Custom Extension
Copy CopyRun External Link
1
2<script async src="https://cdn.fastcomments.com/js/embed-v2-async.min.js"></script>
3<div id="fastcomments-widget"></div>
4<script>
5window.fcConfigs = [{
6 "tenantId": "demo",
7 "extensions": [
8 {
9 "id": "my-extension",
10 "path": "https://example.com/my-extension.min.js"
11 }
12 ]
13}];
14</script>
15

關於功能範例,請參閱下一節。

擴充套件物件 Internal Link

擴充功能物件由以下定義組成:

擴充功能物件 JSDoc
Copy CopyRun External Link
1
2/**
3 * FastCommentsUI 擴充功能物件。用於延遲載入特定元件。例如,並非所有客戶都會使用評論系統,
4 * 因此我們只在需要時才載入該擴充功能。
5 *
6 * @typedef {Object} FastCommentsUIExtension
7 * @property {string} id
8 * @property {Element} scriptNode
9 * @property {Element} root - 小工具的根 DOM 節點。
10 * @property {string} [css]
11 * @property {Object} config - FastComments 設定物件。
12 * @property {Object} commentsById - 對包含所有按 id 索引之留言之物件的參照,並會持續保持最新。
13 * @property {Object} translations - 對所有翻譯的參照。
14 * @property {Function} reRenderComment - 對一個函式的參照,可呼叫該函式重新渲染一則留言。
15 * @property {Function} removeCommentAndReRender - 對一個函式的參照,可呼叫該函式從記憶體中移除留言並重新渲染 DOM 的相應部分。
16 * @property {Function} newBroadcastId - 對一個函式的參照,可呼叫該函式建立新的 broadcast id 並將其加入本地要忽略的 broadcast id 清單。
17 * @property {Function} [withSessionId] - 傳入請求 URL 與回呼函式呼叫。當第三方 Cookie 被封鎖時(與留言及投票請求使用的路徑相同),會以附加了檢視者 session id 的 URL 呼叫回呼函式,否則 URL 保持不變。若無法透過該方式取得 session id,則附加最後一次傳給 rememberAnonSessionId 的匿名 session id(如果有的話)。不適用於 SSO。
18 * @property {Function} [rememberAnonSessionId] - 傳入回應所回傳的 anonSessionId 呼叫。伺服器只會對請求中沒有 session cookie 的訪客回傳此值,且絕不會對已登入的工作階段回傳。它會保存在 localStorage 中(當儲存空間不可用時則保存在記憶體中),以便 withSessionId 在後續請求中傳送它,讓訪客保持同一個身分。
19 * @property {FastCommentsUIExtensionSetupEventHandlers} [setupEventHandlers]
20 * @property {FastCommentsUIExtensionPrepareCommentForSavingCallback} [prepareCommentForSaving] - 在留言即將發佈時呼叫。回傳 false 可取消送出(例如當附加的投票尚未填寫完成時)。
21 * @property {FastCommentsUIExtensionNewCommentCallback} [newComment]
22 * @property {FastCommentsUIExtensionReplyAreaFilter} [replyAreaFilter] - 過濾留言區的 HTML。
23 * @property {FastCommentsUIExtensionWidgetFilter} [widgetFilter] - 在渲染時過濾整個小工具的 HTML。
24 * @property {FastCommentsUIExtensionCommentTopFilter} [commentFilter] - 在渲染前過濾每則留言的 HTML。
25 * @property {FastCommentsUIExtensionReplyAreaFilter} [commentMenuFilter] - 在渲染前過濾每則留言選單的 HTML。
26 * @property {FastCommentsUIExtensionMenuFilter} [menuFilter] - 在渲染時過濾整個小工具的 HTML。
27 * @property {FastCommentsUIExtensionReplyAreaTop} [replyAreaTop] - (舊版) 回傳要加入回覆區頂部的 HTML。
28 * @property {FastCommentsUIExtensionWidgetTopCallback} [widgetTop] - (舊版) 回傳要加入小工具頂部的 HTML。
29 * @property {FastCommentsUIExtensionCommentTopCallback} [commentTop] - (舊版) 回傳要加入留言元素頂部的 HTML。
30 * @property {FastCommentsUIExtensionCommentBottomCallback} [commentBottom] - (舊版) 回傳要加入留言元素底部的 HTML。
31 * @property {FastCommentsUIExtensionCommentBottomCallback} [commentContentBottom] - 回傳要加在留言文字之後、位於留言內容元素內的 HTML(由投票功能使用)。
32 * @property {Function} [replyAreaInputBottom] - 回傳要加入留言輸入框內、位於文字輸入框下方的 HTML(投票功能用它來放置就地投票編輯器)。會接收父留言 id,根回覆框則為 null。
33 * @property {Function} [onPollUpdate] - 當頁面上投票的票數變動時,會以即時事件呼叫。
34 * @property {Function} isSiteAdmin - 回傳檢視者是否為該租戶的管理員或版主。在第一次請求後即可得知。
35 * @property {FastCommentsUIExtensionMenuBottomCallback} [menuBottom] - (舊版) 回傳要加入每則留言選單元素底部的 HTML。
36 * @property {FastCommentsUIExtensionRenderCallback} [onRender]
37 * @property {FastCommentsUIExtensionConnectionStatusCallback} [onLiveConnectionStatusUpdate]
38 * @property {FastCommentsUIExtensionInitialRenderCallback} [onInitialRenderComplete]
39 * @property {FastCommentsUIExtensionPresenceUpdateCallback} [onPresenceUpdate]
40 */
41
42/**
43 * @callback FastCommentsUIExtensionSetupEventHandlers
44 * @param {Element} element - 根元素。
45 * @param {Object.<string, Function>} clickListeners - 點擊事件處理器,以類別名稱為索引,可透過參照修改。
46 * @returns void
47 */
48
49/**
50 * @callback FastCommentsUIExtensionWidgetTopCallback
51 * @param {Object} moduleData
52 * @returns {string}
53 */
54
55/**
56 * @callback FastCommentsUIExtensionWidgetFilter
57 * @param {Object} moduleData
58 * @param {Object} html
59 * @returns {string}
60 */
61
62/**
63 * @callback FastCommentsUIExtensionCommentTopCallback
64 * @param {Object} comment
65 * @returns {string}
66 */
67
68/**
69 * @callback FastCommentsUIExtensionCommentTopFilter
70 * @param {Object} comment
71 * @param {string} html
72 * @returns {string}
73 */
74
75/**
76 * @callback FastCommentsUIExtensionCommentBottomCallback
77 * @param {Object} comment
78 * @returns {string}
79 */
80
81/**
82 * @callback FastCommentsUIExtensionMenuBottomCallback
83 * @param {Object} comment
84 * @returns {string}
85 */
86
87/**
88 * @callback FastCommentsUIExtensionMenuFilter
89 * @param {Object} comment
90 * @param {string} html
91 * @returns {string}
92 */
93
94/**
95 * @callback FastCommentsUIExtensionRenderCallback
96 * @returns {string}
97 */
98
99/**
100 * @callback FastCommentsUIExtensionConnectionStatusCallback
101 * @param {boolean} isConnected
102 * @returns {void}
103 */
104
105/**
106 * @callback FastCommentsUIExtensionInitialRenderCallback
107 * @returns {void}
108 */
109
110/**
111 * @callback FastCommentsUIExtensionReplyAreaTop
112 * @param {Object|null} currentUser
113 * @param {boolean} isSaving
114 * @param {boolean} isReplyOpen
115 * @param {string|null} parentId
116 * @returns {string}
117 */
118
119/**
120 * @callback FastCommentsUIExtensionReplyAreaFilter
121 * @param {Object|null} currentUser
122 * @param {boolean} isSaving
123 * @param {boolean} isReplyOpen
124 * @param {string|null} parentId
125 * @param {string|null} html
126 * @returns {string}
127 */
128
129/**
130 * @callback FastCommentsUIExtensionPrepareCommentForSavingCallback
131 * @param {Object} comment
132 * @param {string} parentId
133 */
134
135/**
136 * @callback FastCommentsUIExtensionNewCommentCallback
137 * @param {Object} comment
138 */
139
140/**
141 * @callback FastCommentsUIExtensionPresenceUpdateCallback
142 * @param {Object} update
143 */
144

擴充套件 API Internal Link

與 Extension 的互動很簡單,只要定義我們想呼叫的函式參考。

延續先前的範例,假設我們想在每個評論的頂部加入 HTML:

簡單的擴充功能 - 繼續
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6
7 extension.commentFilter = function(comment, html) {
8 return `<h3>The user's name is ${comment.commenterName}!</h3>` + html;
9 }
10})();
11

每當你返回這類 HTML 時,它會透過 DOM 差異比對演算法合併到 UI 中。

手動觸發評論的重新渲染

我們可以等到初始頁面載入完成,並透過呼叫 reRenderComment 手動重新渲染評論:

重新渲染評論
Copy CopyRun External Link
1
2(function () {
3 const extension = FastCommentsUI.extensions.find((extension) => {
4 return extension.id === 'my-extension';
5 });
6
7 let renderCount = 0;
8
9 extension.commentFilter = function(comment, html) {
10 renderCount++;
11 return `<h3>The render count is ${renderCount}!</h3>` + html;
12 }
13
14 extension.onInitialRenderComplete = function() {
15 setInterval(function() {
16 extension.reRenderComment(extension.commentsById[Object.keys(extension.commentsById)[0]], function renderDone() {
17 console.log('Comment re-render done.');
18 });
19 }, 2000); // timeout not required, just an example.
20 }
21})();
22