如何在Astro中将服务端变量传递到客户端JS?
解决Astro组件中注入Google OAuth变量的问题
核心方案:利用Astro内联脚本模板传递变量
在Astro中,直接在<script>标签使用模板语法需要配合is:inline指令,或通过全局变量注入方式传递后端参数,以下是适配后的完整Astro组件:
--- // 从后端接收凭证变量,可通过父组件传递或环境变量获取 const { CLIENT_ID, API_KEY } = Astro.props; --- <div> <p>Google Calendar API Quickstart</p> <!-- 授权与登出按钮 --> <button id="authorize_button" onclick="handleAuthClick()">Authorize</button> <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button> <pre id="content" style="white-space: pre-wrap;"></pre> <!-- 先注入全局变量,确保业务脚本可读取 --> <script is:inline> window.GOOGLE_CREDENTIALS = { CLIENT_ID: '{CLIENT_ID}', API_KEY: '{API_KEY}' }; </script> <!-- 业务逻辑脚本,读取全局变量 --> <script is:inline> /* exported gapiLoaded */ /* exported gisLoaded */ /* exported handleAuthClick */ /* exported handleSignoutClick */ // 从全局对象获取凭证 const CLIENT_ID = window.GOOGLE_CREDENTIALS.CLIENT_ID; const API_KEY = window.GOOGLE_CREDENTIALS.API_KEY; const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'; const SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'; let tokenClient; let gapiInited = false; let gisInited = false; document.getElementById('authorize_button').style.visibility = 'hidden'; document.getElementById('signout_button').style.visibility = 'hidden'; function gapiLoaded() { gapi.load('client', intializeGapiClient); } async function intializeGapiClient() { await gapi.client.init({ apiKey: API_KEY, discoveryDocs: [DISCOVERY_DOC], }); gapiInited = true; maybeEnableButtons(); } function gisLoaded() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: '', }); gisInited = true; maybeEnableButtons(); } function maybeEnableButtons() { if (gapiInited && gisInited) { document.getElementById('authorize_button').style.visibility = 'visible'; } } function handleAuthClick() { tokenClient.callback = async (resp) => { if (resp.error !== undefined) { throw (resp); } document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').innerText = 'Refresh'; await listUpcomingEvents(); }; if (gapi.client.getToken() === null) { tokenClient.requestAccessToken({prompt: 'consent'}); } else { tokenClient.requestAccessToken({prompt: ''}); } } function handleSignoutClick() { const token = gapi.client.getToken(); if (token !== null) { google.accounts.oauth2.revoke(token.access_token); gapi.client.setToken(''); document.getElementById('content').innerText = ''; document.getElementById('authorize_button').innerText = 'Authorize'; document.getElementById('signout_button').style.visibility = 'hidden'; } } async function listUpcomingEvents() { let response; try { const request = { 'calendarId': 'primary', 'timeMin': (new Date()).toISOString(), 'showDeleted': false, 'singleEvents': true, 'maxResults': 10, 'orderBy': 'startTime', }; response = await gapi.client.calendar.events.list(request); } catch (err) { document.getElementById('content').innerText = err.message; return; } const events = response.result.items; if (!events || events.length == 0) { document.getElementById('content').innerText = 'No events found.'; return; } const output = events.reduce( (str, event) => `${str}${event.summary} (${event.start.dateTime || event.start.date})\n`, 'Events:\n'); document.getElementById('content').innerText = output; } </script> <!-- Google官方API脚本 --> <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script> <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script> </div>
关键细节说明
is:inline指令作用:Astro默认会打包处理脚本,添加该指令后脚本会直接渲染到HTML中,允许使用模板语法注入变量。- 全局变量传递逻辑:先通过内联脚本将后端传入的凭证挂载到
window,确保业务脚本加载时变量已存在。 - 变量接收方式:组件顶部通过
Astro.props接收参数,也可直接使用环境变量(如import.meta.env.GOOGLE_CLIENT_ID)。
替代方案:通过DOM data属性传递变量
若不想使用全局变量,可将变量存储在DOM元素的data属性中,再在业务脚本中读取:
--- const { CLIENT_ID, API_KEY } = Astro.props; --- <div id="calendar-auth-container" data-client-id="{CLIENT_ID}" data-api-key="{API_KEY}"> <!-- ... 按钮与内容区域 ... --> </div> <script is:inline> const container = document.getElementById('calendar-auth-container'); const CLIENT_ID = container.dataset.clientId; const API_KEY = container.dataset.apiKey; // ... 后续业务逻辑 ... </script>
内容的提问来源于stack exchange,提问作者ThomasReggi
相关产品推荐
相关产品推荐

