Vite+React中正确加载Google登录脚本的方法咨询
Vite + React + TS 中Google登录按钮的实现问题
问题描述
使用Vite搭建React+TS应用,尝试添加Google登录按钮。按照官方指南在index.html中直接引入GSI脚本时按钮无法显示,代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <script src="https://accounts.google.com/gsi/client" async></script> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body> </html>
改用useEffect动态加载脚本后按钮可以显示,但认为该方式不够规范,希望了解更标准的实现方式,以及直接在index.html加载脚本失败的原因。
原因分析:为何index.html直接加载脚本无效
Google GSI脚本加载完成后会立即扫描页面中的g_id_onload和g_id_signin元素,你的实现存在两个关键问题:
- 脚本执行时机过早:
async属性让脚本异步加载,大概率会在React完成组件挂载(即渲染出Google登录相关DOM元素)前执行完毕,此时脚本找不到目标元素,无法渲染按钮。 - 脚本位置错误:脚本放在
<div id="root">之前,React挂载组件是在后续的main.tsx执行阶段,脚本执行时目标DOM还未生成。
标准实现方式
1. 调整index.html脚本加载时机(最简单修复)
将GSI脚本移至React入口脚本之后,并使用defer属性确保脚本在DOM解析完成后执行,同时保证React已渲染出目标元素:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> <!-- 移至React脚本后,用defer保证DOM就绪后执行 --> <script src="https://accounts.google.com/gsi/client" defer></script> </body> </html>
登录组件无需修改脚本加载逻辑,直接保留Google的DOM元素即可(注意修正htpp拼写错误为http):
export default function SignInButton() { return ( <div> <div id="g_id_onload" data-client_id="你的客户端ID" data-context="signin" data-ux_mode="popup" data-login_uri="http://localhost:5173" data-auto_prompt="false" ></div> <div className="g_id_signin" data-type="standard" data-shape="rectangular" data-theme="outline" data-text="signin_with" data-size="large" data-logo_alignment="left" ></div> </div> ); }
2. 使用官方React库(推荐)
Google提供了适配React生态的官方库@react-oauth/google,完全遵循组件化思想,无需手动处理脚本加载:
步骤1:安装依赖
npm install @react-oauth/google
步骤2:在入口文件初始化Provider
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { GoogleOAuthProvider } from '@react-oauth/google'; ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <GoogleOAuthProvider clientId="你的客户端ID"> <App /> </GoogleOAuthProvider> </React.StrictMode>, );
步骤3:在组件中使用登录按钮
import { GoogleLogin } from '@react-oauth/google'; export default function SignInButton() { const handleSuccess = (response: any) => { // 处理登录成功逻辑,如获取ID Token console.log('登录成功:', response); }; const handleError = () => { console.log('登录失败'); }; return ( <GoogleLogin onSuccess={handleSuccess} onError={handleError} uxMode="popup" theme="outline" size="large" text="signin_with" shape="rectangular" logoAlignment="left" /> ); }
3. 优化动态加载脚本的实现(自定义场景)
如果需要自定义脚本加载逻辑,可优化为只加载一次脚本,并在脚本加载完成后手动初始化按钮:
import { useEffect, useRef } from "react"; export default function SignInButton() { const isScriptLoaded = useRef(false); useEffect(() => { if (isScriptLoaded.current) return; const script = document.createElement("script"); script.src = "https://accounts.google.com/gsi/client"; script.async = true; script.onload = () => { // 脚本加载完成后手动初始化并渲染按钮 window.google?.accounts.id.initialize({ client_id: "你的客户端ID", callback: (response) => console.log('登录回调:', response), }); window.google?.accounts.id.renderButton( document.getElementById("g_id_signin")!, { type: "standard", shape: "rectangular", theme: "outline", text: "signin_with", size: "large", logo_alignment: "left", } ); }; document.body.appendChild(script); isScriptLoaded.current = true; return () => { document.body.removeChild(script); isScriptLoaded.current = false; }; }, []); return <div id="g_id_signin"></div>; }
内容的提问来源于stack exchange,提问作者James Huang
相关产品推荐
相关产品推荐

