用 WordPress REST API 顯示最新文章

想在首頁、品牌頁或活動頁自動顯示最新文章,可以讓前端 JavaScript 透過 WordPress REST API 讀取已發佈內容,再把標題、日期、摘要、精選圖片與文章連結排成列表。新文章發布後,列表會跟著資料更新,不必回到 Vibe 畫布逐篇修改。

這篇教學提供兩種做法。第一種先用內建工具產生 Prompt,交給常用的 AI 製作專屬版面;第二種直接使用本文的橫向列表範例,再依品牌調整顏色與文章數量。

完成後的文章列表#

範例採單欄橫向列表。每篇文章的精選圖片固定為 16:9,標題與摘要保留足夠寬度;手機版會自動改成圖片在上,文字在下的排列。

WordPress REST API 最新文章橫向列表範例,精選圖片使用 16 比 9 比例

先用工具產生專屬 Prompt#

填入自己的 WordPress 網址,再選擇文章數量、列表樣式、品牌主色與精選圖片設定。按下「產生 Prompt」後,複製完整內容貼給 ChatGPT、Claude、Gemini 或其他程式設計 AI。

AI Prompt 產生器

產生專屬文章列表 Prompt#

填入 WordPress 網址與顯示方式,再把產生的內容貼給 ChatGPT、Claude、Gemini 或其他程式設計 AI。

AI 回覆時,應把程式碼分成「Vibe 畫布的 HTML 與 CSS」及「頁面層級頁尾腳本的 JavaScript」兩段。先保留 AI 產生的兩段內容,再依後面的貼上位置完成設定。

直接使用橫向列表範例#

想直接套用本文範例時,依序複製下面兩段程式碼。HTML 與 CSS 留在 Vibe 畫布,JavaScript 放在頁面層級的頁尾腳本,能避免腳本太早執行而找不到文章列表容器。

Step 1:貼上 HTML 與 CSS#

開啟要顯示文章的 Vibe 畫布,把下列整段貼到內容區。這一段會建立標題、載入提示、文章列表容器與響應式外觀。

HTML + CSS
<section class="sn-api-posts" data-site-url="" aria-labelledby="sn-api-posts-title">
  <div class="sn-api-posts__head">
    <p class="sn-api-posts__eyebrow">網站新消息</p>
    <h2 id="sn-api-posts-title">最新文章</h2>
    <p>新文章發布後,這個列表會自動更新。</p>
  </div>

  <p id="sn-api-posts-status" class="sn-api-posts__status" role="status" aria-live="polite">文章載入中……</p>
  <div id="sn-api-posts-list" class="sn-api-posts__list"></div>
</section>

<style>
  .sn-api-posts {
    --brand: #0a6eb1;
    --text: #28333b;
    --copy: #59636b;
    width: min(1120px, calc(100% - 40px));
    margin: 56px auto;
    color: var(--text);
    font-family: "Noto Sans TC", system-ui, sans-serif;
  }

  .sn-api-posts,
  .sn-api-posts * { box-sizing: border-box; }
  .sn-api-posts__head { margin-bottom: 24px; }
  .sn-api-posts__eyebrow { margin: 0 0 8px; color: var(--brand); font-weight: 800; }
  .sn-api-posts__head h2 { margin: 0 0 10px; font-size: clamp(32px, 5vw, 48px); }
  .sn-api-posts__head > p:last-child { margin: 0; color: var(--copy); font-size: 17px; }
  .sn-api-posts__status { min-height: 24px; color: var(--copy); }
  .sn-api-posts__list { display: grid; gap: 16px; }

  .sn-api-post-item {
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
    overflow: hidden;
    border: 1px solid rgba(10, 110, 177, 0.14);
    border-radius: 18px;
    background: #fff;
    box-shadow: 0 12px 32px rgba(34, 75, 100, 0.08);
  }

  .sn-api-post-item__media {
    width: 100%;
    aspect-ratio: 16 / 9;
    align-self: center;
    overflow: hidden;
    background: linear-gradient(135deg, #e7f5fe, #d9fbf8);
  }

  .sn-api-post-item__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .sn-api-post-item__placeholder {
    display: grid;
    width: 100%;
    height: 100%;
    place-items: center;
    color: var(--brand);
    font-weight: 800;
  }

  .sn-api-post-item__body { align-self: center; padding: 18px 22px; }
  .sn-api-post-item__date { color: var(--brand); font-size: 13px; font-weight: 800; }
  .sn-api-post-item__title { margin: 7px 0 8px; font-size: 21px; line-height: 1.42; }
  .sn-api-post-item__excerpt {
    display: -webkit-box;
    margin: 0 0 10px;
    overflow: hidden;
    color: var(--copy);
    line-height: 1.65;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
  .sn-api-post-item__link { color: var(--brand); font-weight: 800; text-decoration: none; }
  .sn-api-post-item__link:hover { text-decoration: underline; }

  @media (max-width: 680px) {
    .sn-api-posts { width: min(100% - 28px, 1120px); margin: 36px auto; }
    .sn-api-post-item { grid-template-columns: 1fr; }
    .sn-api-post-item__media { aspect-ratio: 16 / 9; }
  }
</style>

Step 2:設定文章來源#

同一個網站顯示自己的文章時,保留 data-site-url="" 即可,程式會自動使用目前網域。若要顯示另一個 WordPress 網站的文章,把該屬性改成完整網址,例如 data-site-url="https://example.com",網址結尾不加斜線。

開啟「即站力 Site Now 頁面設定 → 自訂程式碼片段 → 頁尾腳本」,把下列整段貼入欄位。預設顯示 4 篇文章;需要調整數量時,修改 postCount 後面的數字。

JavaScript
<script>
  (() => {
    const section = document.querySelector('.sn-api-posts');
    const list = document.querySelector('#sn-api-posts-list');
    const status = document.querySelector('#sn-api-posts-status');

    if (!section || !list || !status) return;

    const siteUrl = section.dataset.siteUrl.trim() || window.location.origin;
    const postCount = 4;
    const apiUrl = `${siteUrl.replace(/\/$/, '')}/wp-json/wp/v2/posts?per_page=${postCount}&_embed=wp:featuredmedia&_fields=id,date,link,title,excerpt,featured_media,_links,_embedded`;

    const htmlToText = (html) => {
      const doc = new DOMParser().parseFromString(html || '', 'text/html');
      return doc.body.textContent.trim();
    };

    const makeElement = (tag, className, text) => {
      const element = document.createElement(tag);
      element.className = className;
      if (text) element.textContent = text;
      return element;
    };

    const makeMedia = (post, titleText) => {
      const mediaBox = makeElement('div', 'sn-api-post-item__media');
      const media = post._embedded?.['wp:featuredmedia']?.[0];

      if (!media?.source_url) {
        mediaBox.append(makeElement('span', 'sn-api-post-item__placeholder', '最新文章'));
        return mediaBox;
      }

      const image = makeElement('img', 'sn-api-post-item__image');
      image.src = media.source_url;
      image.alt = media.alt_text || titleText;
      image.loading = 'lazy';
      image.width = 800;
      image.height = 450;
      mediaBox.append(image);
      return mediaBox;
    };

    const renderPost = (post) => {
      const item = makeElement('article', 'sn-api-post-item');
      const titleText = htmlToText(post.title.rendered);
      const body = makeElement('div', 'sn-api-post-item__body');
      const dateText = new Intl.DateTimeFormat('zh-TW', { dateStyle: 'medium' }).format(new Date(post.date));
      const title = makeElement('h3', 'sn-api-post-item__title', titleText);
      const excerpt = makeElement('p', 'sn-api-post-item__excerpt', htmlToText(post.excerpt.rendered));
      const link = makeElement('a', 'sn-api-post-item__link', '閱讀文章 →');

      link.href = post.link;
      link.target = '_blank';
      link.rel = 'noopener';
      body.append(makeElement('time', 'sn-api-post-item__date', dateText), title, excerpt, link);
      item.append(makeMedia(post, titleText), body);
      return item;
    };

    fetch(apiUrl)
      .then((response) => {
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return response.json();
      })
      .then((posts) => {
        const fragment = document.createDocumentFragment();
        posts.forEach((post) => fragment.append(renderPost(post)));
        list.replaceChildren(fragment);
        status.hidden = posts.length > 0;
        status.textContent = posts.length > 0 ? '' : '目前還沒有可顯示的文章。';
        section.dataset.loaded = 'true';
      })
      .catch((error) => {
        console.error('文章列表載入失敗:', error);
        status.hidden = false;
        status.textContent = '文章暫時無法載入,請稍後重新整理。';
        section.dataset.loaded = 'error';
      });
  })();
</script>

Step 4:更新並查看前台#

按右上角「更新」,再開啟前台頁面。載入提示消失後,列表會顯示最新文章;點選「閱讀文章 →」可在新分頁開啟原文。若剛更新的程式碼尚未出現,先做一次強制重新整理再查看。

調整文章數量與品牌色#

  • 文章數量:在頁尾腳本找到 const postCount = 4;,改成需要的數字。
  • 品牌主色:在 Vibe 畫布程式碼找到 --brand: #0a6eb1;,換成自己的品牌色碼。
  • 區塊標題:直接修改「網站新消息」「最新文章」與下一行說明文字。
  • 圖片尺寸:範例已用 aspect-ratio: 16 / 9; 固定比例,保留這行即可維持整齊列表。

使用前注意事項#

  • 這個範例讀取公開文章,不需要把 WordPress 帳號、密碼或 API 金鑰放進前端程式碼。
  • 來源網站需能開啟 /wp-json/wp/v2/posts。網站的安全設定需允許公開 REST API,文章列表才能載入。
  • 跨網域讀取時,來源網站還需允許瀏覽器的跨來源請求。同站顯示自己的文章最單純,也較不容易受到來源網站設定影響。
  • 這種前端列表適合即時展示內容。若文章列表是頁面的主要 SEO 內容,仍應保留可被搜尋引擎直接讀取的標題、說明與內部連結。