Clojure Sente:Sente 在页面加载后向客户端发送后续消息并销毁视图

Clojure Sente: Sente sends a follow up message to the client after page load and destroys the view

我很困惑为什么 sente 在页面最初加载后大约 8 秒后自行向客户端发送一条消息。还有什么奇怪的是,如果我在发生这种情况之前通过 websocket 快速向服务器发送消息,一切都会稳定下来并且视图不会崩溃。

作为参考,前端是带有试剂的 clojurescript,它是一个 luminus 项目。为了进一步参考,它几乎完全是 "web development with clojure" 第 5 章中的示例应用程序。

我知道是服务器向客户端推送消息导致了问题,我只是对 Sente 了解不够,无法理解为什么它会这样做。

这是我认为相关的代码:

服务器端:

(defn save-message! [message]
  (if-let [errors (validate-message message)]
    {:errors errors}
    (do
      (db/save-message! message)
      message)))

(defn handle-message! [{:keys [id client-id ?data]}]
  (when (= id :guestbook/add-message)
    (let [response (-> ?data
                       (assoc :timestamp (java.util.Date.))
                       save-message!)]
      (if (:errors response)
        (chsk-send! client-id [:guestbook/error response])
        (doseq [uid (:any @connected-uids)]
          (chsk-send! uid [:guestbook/add-message response]))))))

客户端(带试剂):

(defn response-handler [messages fields errors]
  (fn [{[_ message] :?data}]
    (if-let [response-errors (:errors message)]
      (reset! errors response-errors)
      (do
        ;; Fires right before the view crashes!
        (.log js/console "response-handled")
        (reset! errors nil)
        (reset! fields nil)
        (swap! messages conj message)))))

(defn home []
  (let [messages (atom nil)
        fields (atom nil)
        errors (atom nil)]
    (ws/start-router! (response-handler messages fields errors))
    (get-messages messages)
    (fn []
      [:div
       [:div.row
        [:div.span12
         [message-list messages]]]
       [:div.row
        [:div.span12
         [message-form fields errors]]]])))

问题是,当 sente 自己发送消息时,没有数据来更新消息(或者至少这是我最好的猜测),因此原子的字段变为空并且 reagent (react.js) 抛出尝试从 vdom 中区分和修补。

如果有人知道 sente 在做什么,将不胜感激。当您使用 Immutant 的异步套接字支持并自己完成大量工作(serialize/deserialize、处理连接等)时,这种完全相同的设置工作正常。

;;;;;;;

作为跟进,我通过过滤非零消息解决了这个问题:

(defn response-handler [messages fields errors]
  (fn [{[_ message] :?data}]
    (if-let [response-errors (:errors message)]
      (reset! errors response-errors)
      (when (not= message nil)
        (reset! errors nil)
        (reset! fields nil)
        (swap! messages conj message)))))

不过,这是一种绷带解决方案,如果不立即使用套接字,我很想知道为什么 Sente 在页面加载后向我抛出消息。

您可以通过查看开发人员工具中的网络选项卡来检查发生了什么; websocket 框架应该有一个子选项卡。

Sente 自己发送一些事件,如果我没记错的话,事件名称(一个关键字,它是事件向量的第一个元素)在 chsk 命名空间中。我相信您无论如何都应该对事件名称使用某种调度,而不是假设只有一种事件会到达。

在 re-frame 的上下文中,我一直在过滤不需要的事件并将其余事件分派到 re-frame 事件循环。我想你可以在 luminus 中做类似的事情。在服务器端,我一直在类似的设置中使用 multimethods。