卸载前调用服务器端函数(firebase)

Call server side function before unload (firebase)

我正在尝试在触发 JavaScript 事件“beforeunload”时保存用户数据。我调用了一个服务器端函数(firebase 云函数),它只是从用户那里获取一个对象并更新一个文档。该函数确实触发了,但从 firebase 控制台我可以看到它以代码 204 结束。 这是我的客户端代码

window.onbeforeunload = function() {
    firefun_ref(data);
}

服务器端:

exports.updateStats = functions.https.onCall(async (data, context) => {
  return admin.firestore().collection("stats")
    .doc((new Date()).toISOString().substring(0, 10))
    .update(filter(data));
});

我想指出的是,如果我 运行 函数本身它工作正常,它只是在卸载事件之前出现问题

这是不可能的。

浏览器严格限制了您在 beforeunload 活动中可以做的事情,因为它很容易被滥用。不允许异步事件(因为它们可能会强制 window 在用户想要关闭它后保持打开任意时间。)

据我所知,onbeforeunload 唯一可靠的用法是显示一个提示,让用户改变主意;有些浏览器会让您自定义显示的文本(通过从 beforeunload 处理程序返回一个字符串),有些浏览器将始终显示默认文本:

window.addEventListener('beforeunload', function (e) {
  // Cancel the event
  e.preventDefault(); // If you prevent default behavior in Mozilla Firefox prompt will always be shown
  // Chrome requires returnValue to be set
  e.returnValue = '';
});

https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload

如果您要写入的数据在用户关闭选项卡之前已知,您可以在实时数据库(属于 Firebase 的另一个数据库)中注册 so-called onDisconnect handler

onDisconnect 处理程序是一个延迟的写入操作,一旦服务器注意到客户端已断开连接,它就会立即运行,这可能是因为客户端中的 SDK 在断开连接之前通知它,也可能是因为套接字超时服务器(可能需要几分钟)。

Firestore 上不存在等效协议,因为它的有线协议是无连接的,而实时数据库基于(网络)套接字工作。如果您想将其集成到 Firestore 中,请查看 building a presence system in Firestore by using the Realtime Database.

上的文档

我试图让它与 Beacon API 一起工作,甚至找到了一个钩子:

/*
  see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
  
  ```jsx
  const MyComponent = ({ children }) => {
    // will get fired if the page unloads while the component is mounted
    useUnloadBeacon({
      url: 'https://api.my.site/route',
      payload: () => {
        return 'something'
      }
    })
    
    return children
  }
  ```
*/

import { useEffect } from "react"

export const useUnloadBeacon = ({
  url = "",
  payload = () => {},
}) => {
  const eventHandler = () => navigator.sendBeacon(url, payload())

  useEffect(() => {
    window.addEventListener("unload", eventHandler, true)
    return () => {
      window.removeEventListener("unload", eventHandler, true)
    }
  }, [])
}

我面临的唯一问题是触发云功能。我正在发送一个获取请求,但不知何故没有触发。

云函数

app.post("/setStatusToOpen", setStatusToOpen)

const setStatusToOpen = async (req, res) => {
  console.log("setStatusToOpen, req.query", req.query)
  const { documentId } = req.query
  try {
    const setPayload = { status: "open" }
    firestore
      .doc(`messageInABottle/${documentId}`)
      .set(setPayload, { merge: true })

    return res.send(true)
  } catch (error) {
    console.log("Error in getMessageInABottleRecipient:", error)
    return res.send(error)
  }
}

我认为这与headers不正确有关。