Vue 3 集成 Telegram WebApp:主题、视口稳定与安全数据传输
译注:本文编译自 dev.to 社区文章《Integrate Telegram WebApp in Vue 3: Theme, ViewportStable, and Secure Data Transfer》,作者演示了如何在 Vue 3 单文件组件中加载 Telegram WebApp SDK、读取 URL 传入的 initData、应用当前主题、锁定视口,并通过
window.Telegram.WebApp.sendData向机器人发送自定义数据。原文链接见文末。
Telegram Mini App 正在成为机器人生态中承载交互界面的主流方式。本文以一个 Vue 3 单文件组件为例,完整走通「前端加载 SDK → 应用主题与视口 → 发送数据 → 后端校验 initData」的最小闭环。需要说明的是,原文不涉及支付处理、内联键盘或长轮询,聚焦点严格限定在 Mini App 的集成流程本身。
前端:Vue 3 组件初始化
组件在 onMounted 阶段完成三件事:调用 expand() 展开到全高、调用 ready() 与 themeChanged() 应用当前主题、在支持时调用 viewportStable() 锁定视口,避免 iOS/Android 上的缩放与布局跳动。同时通过 initDataUnsafe 暴露初始化数据供后续使用。
<template>
<div id="app">
<h1>Telegram Mini App</h1>
<button @click="sendPayload">Send data</button>
<pre>{{ response }}</pre>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const response = ref('');
function initWebApp() {
if (!window.Telegram?.WebApp) {
console.warn('Telegram WebAPI not available');
return;
}
const tg = window.Telegram.WebApp;
// Expand to full height
tg.expand();
// Apply the current theme (optional but recommended)
tg.ready();
tg.themeChanged(); // triggers if theme already set
// Lock viewport to prevent scaling on iOS/Android
if (tg.viewportStable) {
tg.viewportStable();
}
// Expose initData for later use
tg.initDataUnsafe && console.log('initDataUnsafe', tg.initDataUnsafe);
}
function sendPayload() {
if (!window.Telegram?.WebApp) {
alert('WebApp not ready');
return;
}
const payload = { action: 'demo', ts: Date.now() };
window.Telegram.WebApp.sendData(JSON.stringify(payload));
}
// Listen for data sent from the WebApp (when the bot replies via web_app_data)
function listenForBotData() {
if (!window.Telegram?.WebApp) return;
window.Telegram.WebApp.onEvent('web_app_data', (event) => {
response.value = event.data;
});
}
onMounted(() => {
initWebApp();
listenForBotData();
});
</script>
<style scoped>
#app { font-family: sans-serif; margin: 2rem; }
button { padding: 0.5rem 1rem; font-size: 1rem; }
pre { background: #f4f4f4; padding: 1rem; }
</style>HTML 入口:引入 SDK
在 Vue 应用挂载之前引入 Telegram 官方脚本,页面在 Telegram 客户端内加载后即可访问 window.Telegram.WebApp。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Telegram Mini App Vue</title>
<!-- Telegram WebApp SDK -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>后端:校验 initData 并处理 web_app_data
当 Mini App 调用 sendData 后,机器人会收到类型为 web_app_data 的更新,载荷位于 update.web_app_data.data。在信任这份数据之前,必须用 bot token 校验随附的 initData。原文给出了一段 PHP 实现:解析 key=value 参数、剔除 hash 字段、按字母序拼接 data check string,再用以 bot token 为密钥的 SHA256 HMAC 计算并比对,最后用 hash_equals 做恒定时间比较。
<?php
// validate_initdata.php
$initData = $_POST['initData'] ?? $_GET['initData'] ?? '';
$data = $_POST['data'] ?? '';
if (!$initData || !$data) {
http_response_code(400);
exit('Missing parameters');
}
$botToken = getenv('TELEGRAM_BOT_TOKEN');
if (!$botToken) {
http_response_code(500);
exit('Bot token not configured');
}
/**
* Compute the SHA256 HMAC of the data check string.
* See https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
*/
function checkInitData(string $token, string $initData): bool {
// Parse key=value pairs
parse_str($initData, $params);
$hash = $params['hash'] ?? '';
unset($params['hash']);
// Build data check string sorted alphabetically
ksort($params);
$lines = [];
foreach ($params as $key => $value) {
$lines[] = "$key=$value";
}
$dataCheckString = implode("\n", $lines);
$secretKey = hash('sha256', $token, true);
$calculatedHash = hash_hmac('sha256', $dataCheckString, $secretKey);
return hash_equals($hash, $calculatedHash);
}
if (!checkInitData($botToken, $initData)) {
http_response_code(403);
exit('Invalid initData');
}
// At this point $data is safe to use
error_log("Received valid data: $data");
// Process $data as needed (e.g., store, forward, reply)
echo json_encode(['status' => 'ok']);前后端串联
将前文的 sendPayload 替换为携带 initData 的 AJAX 请求,即可把数据安全地转发到自建后端:
async function sendPayload() {
if (!window.Telegram?.WebApp) {
alert('WebApp not ready');
return;
}
const payload = { action: 'demo', ts: Date.now() };
try {
const resp = await axios.post('https://your-domain.com/validate_initdata.php', {
initData: window.Telegram.WebApp.initDataUnsafe,
data: JSON.stringify(payload)
});
response.value = JSON.stringify(resp.data);
} catch (e) {
response.value = 'Error: ' + e.message;
}
}生产环境注意事项
| 事项 | 说明 |
|---|---|
| HTTPS | Mini App 必须通过有效 TLS 证书提供服务,否则 Telegram 会拦截 WebApp SDK |
| CSP | 若启用内容安全策略,需放行 script src="https://telegram.org" 以及指向后端的 connect-src |
| 幂等性 | 将收到的 initData 哈希(或 webhook 的 update_id)存入 Redis 或数据库,避免同一更新被处理两次 |
| 错误处理 | 始终检查请求 Telegram 的 HTTP 状态,并在后续回复用户时记录 getMe 或 sendMessage 返回的 ok:false |
| 视口 | 在 expand() 之后调用 viewportStable(),可避免带安全区内边距的设备出现布局跳动 |
| 主题更新 | 若需动态适配颜色,在收到 theme_changed 事件时重新调用 themeChanged() |
以上构成一个最小但可用的 Vue 3 Telegram Mini App,能够与机器人安全地交换数据。原文作者同时提示,如需进一步了解 Bot API,可参考其给出的外部文档链接。