簡體   English   中英

為什么我不能將 Clojurescript 函數作為回調傳遞給 Javascript?

[英]Why can't I pass Clojurescript functions as callbacks to Javascript?

我正在嘗試在 Clojurescript/Reagent SPA 中使用 Google 的 recaptcha,如下面的代碼所示。

(ns myapp.captcha
  (:require  [reagent.core :as r]
             [cljs.core.async :refer [<! >! chan]])
  (:require-macros [cljs.core.async.macros :refer [go go-loop]]))

(def captcha-ch (chan))

(defn ^:export data-callback [human-proof]
  (go (>! captcha-ch {:captcha-data human-proof})))

(defn ^:export data-expired-callback []
  (go (>! captcha-ch {:captcha-expired true})))

(defn captcha [site-key]
  (let [grecaptcha-script (doto (.createElement js/document "script")
                            (.setAttribute "id" "grecaptcha-script")
                            (.setAttribute "src" "https://www.google.com/recaptcha/api.js"))
        out-ch (chan)
        comp (r/create-class
              {:component-did-mount (fn [this]
                                      (.appendChild (.-body js/document)
                                                    grecaptcha-script))
               :component-will-unmount (fn [this]
                                         (.removeChild (.-body js/document)
                                                       (.getElementById js/document "grecaptcha-script"))
                                         (go (>! captcha-ch {:exit true})))
               :reagent-render (fn [this]
                                 [:div.g-recaptcha
                                  {:data-sitekey site-key
                                   :data-callback "myapp.captcha.data_callback"
                                   :data-expired-callback "myapp.captcha.data_expired_callback"}])})]
    (go-loop []
      (let [msg (<! captcha-ch)]
        (if-not (:exit msg)
          (>! out-ch msg)
          (recur))))

    {:chan out-ch :comp comp}))

當驗證碼被解決並且應該調用數據回調時,我收到一條錯誤消息:

ReCAPTCHA 找不到用戶提供的函數:myapp.captcha.data_callback

另一方面,如果我從瀏覽器的調試器控制台調用 myapp.captcha.data_callback 函數是可見的並正確執行。

PS:暫時請忽略global chan,那是另外一回事。 為了解決這個問題,我必須顯式調用驗證碼渲染,這使我處於一些顯然與腳本加載順序相關的競爭條件中。 我承認這可能是一種更簡潔的方法,但現在看看這里的問題是什么很有趣。

我有一個解決方法,但這有點麻煩。

我在 recaptcha 腳本之前添加了一個腳本元素。 在此腳本元素中,我定義了將調用轉發到我的 clojurescript 函數的回調。

請參閱下面的代碼。

仍然很高興理解為什么我不能直接使用我的 Clojurescript 回調。

(defn captcha [handler]
  (let [callback-hooks (let [s (.createElement js/document "script")]
                         (.setAttribute s "id" "captcha-callbacks")
                         (set! (.-text s)
                               (str "var captcha_data_callback = function(x) { myapp.captcha.data_callback(x)};"
                                    "var captcha_data_expired_callback = function() { myapp.captcha.data_expired_callback()};"))
                         s)
        grecaptcha-script (doto (.createElement js/document "script")
                            (.setAttribute "id" "grecaptcha-script")
                            (.setAttribute "src" "https://www.google.com/recaptcha/api.js"))
        captcha-div [:div.g-recaptcha
                     {:data-sitekey config/grecaptcha-client-key
                      :data-callback "captcha_data_callback"
                      :data-expired-callback "captcha_data_expired_callback"}]]

    (go-loop []
      (let [msg (<! captcha-ch)]
        (handler msg)

        (if-not (:end msg)
          (recur))))

    (r/create-class
     {:component-did-mount (fn [this]
                             (doto (.-body js/document)
                               (.appendChild callback-hooks)
                               (.appendChild grecaptcha-script)))
      :component-will-unmount (fn [this]
                                (doto (.-body js/document)
                                  (.removeChild (.getElementById js/document "captcha-callbacks"))
                                  (.removeChild (.getElementById js/document "grecaptcha-script")))
                                (go (>! captcha-ch {:end true})))
      :reagent-render (fn [this] captcha-div)})))

我和你的情況一樣,只能按照你列出的方式解決它——通過提供一個將函數調用傳遞給 CLJS 的 JS 腳本。

但是,我發現如果將回調函數定義為對象屬性(在 JS 中),Recaptcha 仍然無法找到該函數,即使該函數確實存在。

我在index.html<head>中定義了一個腳本標簽

<script type="text/javascript">
  // This function will be called correctly when the captcha is loaded
  var onloadCallback = function() { myapp.captcha.onloadCallback() };
  // This function will not be found by recaptcha.js
  var testObject = { onDataCallback: function(x) { myapp.captcha.onDataCallback(x) };
</script>

因此,考慮到這一點,問題似乎不是 ClojureScript 問題,而是 recaptcha 問題。 如果有一種方法可以將 ClojureScript 函數直接導出到其命名空間之外的全局范圍(我不確定這是否可能),那么理論上您應該能夠直接從 recaptcha 訪問您的 CLJS 回調。

希望這可以幫助!

這是因為 Closure 編譯器在編譯期間混淆了你的代碼,包括重命名你的函數。 最簡單的解決方案是首先確保編譯器優化尊重您的函數名稱(或者簡單地禁用優化,例如通過:optimization:none和 shadow cljs。

接下來,您要確保導出要使用的函數。 這是通過^:export完成的,例如: (defn ^:export my-exported-fun []...)

最后,在引用函數時傳遞完整的命名空間,例如myapp.frontend.my-exported-fun

希望這對未來的旅行者有幫助:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM