You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rails中结合Hotwire/Stimulus实现OAuth_2并解决Turbo兼容问题?

用Stimulus解决Turbo下OAuth按钮消失的问题

核心逻辑

Turbo更新页面时会替换DOM节点,第三方OAuth SDK生成的按钮会被清掉,且SDK不会自动重新执行。用Stimulus控制器可以在初始加载、Turbo导航完成这些关键时机,重新加载SDK并渲染按钮,还能控制加载频率避免重复请求。

具体实现步骤

1. 编写Stimulus控制器

新建app/javascript/controllers/oauth_buttons_controller.js,代码如下:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["container"]

  connect() {
    // 组件挂载时初始化按钮
    this.initOAuthButtons()
    // 监听Turbo页面渲染完成事件,确保页面更新后重绘按钮
    document.addEventListener("turbo:render", () => this.initOAuthButtons())
  }

  disconnect() {
    // 清理事件监听,防止内存泄漏
    document.removeEventListener("turbo:render", () => this.initOAuthButtons())
  }

  initOAuthButtons() {
    // 先清空容器,避免重复生成按钮
    this.containerTarget.innerHTML = ""

    // 加载Google OAuth按钮
    this.loadGoogleOAuth()
    // 加载Facebook OAuth按钮
    this.loadFacebookOAuth()
  }

  loadGoogleOAuth() {
    // 检查SDK是否已加载,避免重复请求
    if (window.gapi?.auth2) {
      this.renderGoogleBtn()
      return
    }

    // 动态加载Google SDK
    const script = document.createElement("script")
    script.src = "https://apis.google.com/js/platform.js"
    script.onload = () => {
      window.gapi.load('auth2', () => {
        window.gapi.auth2.init({
          client_id: "你的Google客户端ID" // 替换成实际ID
        }).then(() => this.renderGoogleBtn())
      })
    }
    document.head.appendChild(script)
  }

  renderGoogleBtn() {
    const btn = document.createElement("div")
    btn.className = "g-signin2"
    btn.setAttribute("data-onsuccess", "handleGoogleSignIn") // 替换成你的登录回调
    this.containerTarget.appendChild(btn)
    // 手动触发按钮渲染(SDK加载完成后不会自动扫描新DOM)
    window.gapi.signin2.render(btn)
  }

  loadFacebookOAuth() {
    // 检查SDK是否已加载
    if (window.FB) {
      this.renderFacebookBtn()
      return
    }

    // 动态加载Facebook SDK
    const script = document.createElement("script")
    script.src = "https://connect.facebook.net/en_US/sdk.js"
    script.onload = () => {
      window.FB.init({
        appId: "你的Facebook应用ID", // 替换成实际ID
        cookie: true,
        xfbml: true,
        version: "v18.0"
      })
      this.renderFacebookBtn()
    }
    document.head.appendChild(script)
  }

  renderFacebookBtn() {
    const btn = document.createElement("div")
    btn.className = "fb-login-button"
    btn.setAttribute("data-size", "large")
    btn.setAttribute("data-button-type", "continue_with")
    btn.setAttribute("data-onlogin", "handleFacebookSignIn") // 替换成你的登录回调
    this.containerTarget.appendChild(btn)
    // 手动触发按钮渲染
    window.FB.XFBML.parse(this.containerTarget)
  }
}

2. 在页面中挂载控制器

在需要展示OAuth按钮的位置插入以下HTML:

<div data-controller="oauth-buttons">
  <div data-oauth-buttons-target="container" class="oauth-buttons-wrap"></div>
</div>

3. 补充登录回调函数

在全局作用域(比如app/javascript/application.js)添加登录处理逻辑:

// Google登录回调
window.handleGoogleSignIn = (googleUser) => {
  const token = googleUser.getAuthResponse().id_token
  // 这里写后端验证逻辑,比如把token发给服务器
}

// Facebook登录回调
window.handleFacebookSignIn = () => {
  window.FB.api('/me', { fields: 'name,email' }, (res) => {
    // 处理用户信息,发送到后端验证
  })
}

关键细节

  • 避免重复加载SDK:每次初始化前检查全局对象是否存在,减少无效网络请求。
  • Turbo事件监听:通过turbo:render覆盖所有页面更新场景,确保按钮始终存在。
  • 手动渲染按钮:SDK加载完成后不会自动扫描新添加的DOM,必须手动触发渲染方法。

内容的提问来源于stack exchange,提问作者pzupan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 03:17:36