---
title: "X 嵌入"
description: "服务端渲染的 X（Twitter）嵌入，零客户端 API 调用。"
canonical_url: "https://nuxt-scripts.zhcndoc.com/scripts/x-embed"
last_updated: "2026-06-24T18:14:51.868Z"
---

[X（前身为 Twitter）](https://x.com) 是一个用于分享帖子的社交媒体平台。

Nuxt Scripts 提供了 [`<ScriptXEmbed>`](/scripts/x-embed) 组件，它在服务器端获取推文数据，并通过插槽暴露出来，实现完全的样式控制。所有数据均通过你的服务器代理 —— 不进行任何客户端对 X 的 API 调用。

<script-stats>



</script-stats>

<script-docs :embed="true">



</script-docs>

<callout type="info">

This script's proxy endpoints use [HMAC URL signing](/docs/guides/first-party#proxy-endpoint-security) when you configure a `NUXT_SCRIPTS_PROXY_SECRET`. See the [security guide](/docs/guides/first-party#proxy-endpoint-security) for setup instructions.

</callout>

This registers the required server API routes (`/_scripts/embed/x` and `/_scripts/embed/x-image`) that handle fetching tweet data and proxying images.

## [`<ScriptXEmbed>`](/scripts/x-embed)

`<ScriptXEmbed>` 组件是一个无头组件，功能包括：

- 通过 X 联合 API 在服务器端获取推文数据
- 通过你的服务器代理所有图片以保护隐私
- 通过作用域插槽暴露推文数据，实现自定义渲染
- 缓存响应 10 分钟

### 演示

<code-group>
<x-embed-demo label="输出">



</x-embed-demo>

```vue [基础用法]
<template>
  <ScriptXEmbed tweet-id="1754336034228171055">
    <template #default="{ userName, userHandle, text, datetime, likesFormatted }">
      <div class="border rounded-lg p-4 max-w-md">
        <p class="font-bold">
          {{ userName }} (@{{ userHandle }})
        </p>
        <p>{{ text }}</p>
        <p class="text-gray-500 text-sm">{{ datetime }} - {{ likesFormatted }} 赞</p>
      </div>
    </template>
  </ScriptXEmbed>
</template>
```

<template <span>

样式化推文卡片

</span>

>
<template>
<script-x-embed tweet-id="1754336034228171055">
<template #default="{ userName, userHandle, userAvatar, text, datetime, likesFormatted, repliesFormatted, photos, isVerified }">
<div className="max-w-lg,bg-white,dark:bg-gray-800,rounded-xl,border,p-4">
<div className="flex,items-start,gap-3,mb-3">

![undefined](undefined)<div>
<span className="font-bold">
<binding value="userName">



</binding>
</span>

<span v-if="isVerified" className="text-blue-500,ml-1">

✓

</span>


@<binding value="userHandle">



</binding>
</div>
</div>

<binding value="text">



</binding>



<div v-if="photos?.length" className="mb-3,rounded-xl,overflow-hidden">

![undefined](undefined)

</div>

<div className="flex,items-center,gap-4,text-gray-500,text-sm">
<span>
<binding value="datetime">



</binding>
</span>

<span>
<binding value="repliesFormatted">



</binding>

 条回复

</span>

<span>
<binding value="likesFormatted">



</binding>

 赞

</span>
</div>
</div>
</script-x-embed>
</template>



```text
<template #loading>
  <div class="animate-pulse bg-gray-100 rounded-xl p-4 max-w-lg">
    加载推文中...
  </div>
</template>

<template #error>
  <div class="bg-red-50 border border-red-200 rounded-xl p-4 max-w-lg">
    加载推文失败
  </div>
</template>
```

  

```::

### 属性

`ScriptXEmbed` 组件接受以下属性：

<table>
<thead>
  <tr>
    <th>
      属性
    </th>
    
    <th>
      类型
    </th>
    
    <th>
      默认值
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        tweetId
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      必填
    </td>
    
    <td>
      要嵌入的推文 ID
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        apiEndpoint
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        /api/_scripts/x-embed
      </code>
    </td>
    
    <td>
      获取推文数据的自定义 API 端点
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        imageProxyEndpoint
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        /api/_scripts/x-embed-image
      </code>
    </td>
    
    <td>
      代理图片的自定义端点
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rootAttrs
      </code>
    </td>
    
    <td>
      <code>
        HTMLAttributes
      </code>
    </td>
    
    <td>
      <code>
        {}
      </code>
    </td>
    
    <td>
      根元素属性
    </td>
  </tr>
</tbody>
</table>

### 插槽属性

默认插槽接收以下属性：

```ts
interface SlotProps {
  // 原始数据
  tweet: XEmbedTweetData
  // 用户信息
  userName: string
  userHandle: string
  userAvatar: string // 代理 URL
  userAvatarOriginal: string // X 原始 URL
  isVerified: boolean
  // 推文内容
  text: string
  // 格式化后数据
  datetime: string // "12:47 PM · Feb 5, 2024"
  createdAt: Date
  likes: number
  likesFormatted: string // "1.2K"
  replies: number
  repliesFormatted: string // "234"
  // 媒体资源
  photos?: Array<{
    URL: string
    proxiedUrl: string
    width: number
    height: number
  }>
  video?: {
    poster: string
    posterProxied: string
    variants: Array<{ type: string, src: string }>
  }
  // Links
  tweetUrl: string
  userUrl: string
  // Quote tweet
  quotedTweet?: XEmbedTweetData
  // 回复上下文
  isReply: boolean
  replyToUser?: string
  // 辅助函数
  proxyImage: (URL: string) => string
}
```

### 命名插槽

<table>
<thead>
  <tr>
    <th>
      插槽
    </th>
    
    <th>
      描述
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      主内容，带插槽属性
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loading
      </code>
    </td>
    
    <td>
      获取推文数据时展示
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        error
      </code>
    </td>
    
    <td>
      推文数据获取失败时展示，接收 <code>
        { error }
      </code>
    </td>
  </tr>
</tbody>
</table>

## 工作原理

1. **服务器端获取**：在服务器端渲染时从 `cdn.syndication.twimg.com` 获取推文数据
2. **图片代理**：所有图片 URL 都被重写，通过 `/_scripts/embed/x-image` 代理
3. **缓存**：响应在服务器层缓存 10 分钟
4. **无客户端 API 调用**：用户浏览器不会直接联系 X

此方案灵感来源于 [Cloudflare Zaraz 的嵌入实现](https://blog.cloudflare.com/zaraz-supports-server-side-rendering-of-embeds/)。

## 隐私优势

- 不加载第三方 JavaScript
- X 不设置 Cookie
- 浏览器与 X 无直接通信
- 用户 IP 地址不会与 X 共享
- 所有内容均从你的域名提供

<script-types>



</script-types>
</code-group>
