Skip to content

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 暴露初始化数据供后续使用。

vue
<template>
  <div id="app">
    <h1>Telegram Mini App</h1>
    <button @click="sendPayload">Send data</button>
    <pre>&#123;&#123; response &#125;&#125;</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。

html
<!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
<?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 请求,即可把数据安全地转发到自建后端:

javascript
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;
  }
}

生产环境注意事项 ​

事项说明
HTTPSMini 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,可参考其给出的外部文档链接。

原文链接 ​

https://dev.to/serhii_a9c08345ac360cf5c8/integrate-telegram-webapp-in-vue-3-theme-viewportstable-and-secure-data-transfer-21gl