You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Astro组件中通过<script>标签操作props实现动态查询?

解决Astro中动态更新Wiki组件Query参数的问题

核心问题原因

你的Wiki组件当前是服务端渲染/静态生成的,Astro会在编译或服务端请求阶段就把组件渲染成静态HTML,客户端无法直接通过DOM操作(比如document.createElement)来创建或修改Astro组件实例,也不能动态修改props触发组件重新渲染。

推荐解决方案:API路由+客户端JS(适合原生HTML/JS开发者)

这个方案用服务端API处理维基百科数据抓取(避免Cheerio在客户端无法运行的问题),客户端通过表单提交触发API请求,直接更新DOM显示结果,符合你熟悉的开发模式。

步骤1:创建服务端API路由

在src/pages/api/wiki/[query].ts(或.js)中创建API接口,负责抓取维基百科数据:

import { load } from "cheerio";

export async function GET({ params }) {
  const { query } = params;
  try {
    // 编码query避免URL错误
    const encodedQuery = encodeURIComponent(query);
    const response = await fetch(`https://en.wikipedia.org/wiki/${encodedQuery}`);
    
    if (!response.ok) {
      return new Response(JSON.stringify({ error: "维基百科页面不存在" }), { status: 404 });
    }

    const html = await response.text();
    const $ = load(html);
    
    // 提取标题和有效段落(过滤空段落)
    const title = $("h1#firstHeading").text();
    const paragraphs = $("div.mw-content-ltr p")
      .not(".mw-empty-elt")
      .map((_, el) => $(el).text())
      .get()
      .join("\n\n");

    return new Response(JSON.stringify({ title, paragraphs }), {
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "数据抓取失败" }), { status: 500 });
  }
}

步骤2:修改Index.astro实现动态搜索

移除静态的<Wiki>组件,添加结果容器和客户端交互脚本:

---
---

<title>维基百科搜索</title>
<body class="m-5">
  <h1 class="text-3xl text-accent">输入关键词进行搜索:</h1>
  <form id="search-form">
    <input
      id="query"
      type="text"
      name="query"
      class="rounded border-b-[3px] border shadow-md px-3 py-2 w-96 mt-6 focus:outline-none focus:ring focus:ring-accent focus:ring-opacity-30 text-text"
      required
      placeholder="例如:George Washington"
    />
    <button type="submit" class="rounded border-2 px-3 py-2 ml-2">搜索</button>
  </form>
  <br />
  <!-- 搜索结果容器 -->
  <div id="wiki-result" class="border p-4 hidden"></div>

  <script>
    const form = document.getElementById('search-form');
    const resultContainer = document.getElementById('wiki-result');

    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const query = document.getElementById('query').value.trim();
      if (!query) return;

      // 显示加载状态
      resultContainer.textContent = '加载中...';
      resultContainer.classList.remove('hidden');

      try {
        const encodedQuery = encodeURIComponent(query);
        const response = await fetch(`/api/wiki/${encodedQuery}`);
        
        if (!response.ok) {
          throw new Error('请求失败,请检查关键词是否正确');
        }

        const data = await response.json();
        // 更新DOM显示结果
        resultContainer.innerHTML = `
          <h1 class="text-2xl font-bold mb-4">${data.title}</h1>
          <div class="whitespace-pre-line">${data.paragraphs}</div>
        `;
      } catch (error) {
        resultContainer.innerHTML = `<p class="text-red-500">错误:${error.message}</p>`;
      }
    });
  </script>
</body>

进阶方案:使用Astro客户端岛屿(保留组件结构)

如果想保留Wiki组件的封装性,可以将其改为客户端组件,配合Astro状态管理实现动态更新:

修改Wiki.astro为客户端组件

---
const { query } = Astro.props;
let data = { heading: "", paragraphs: "" };

if (Astro.clientAddress) {
  // 客户端用DOMParser替代Cheerio(Cheerio仅支持Node环境)
  try {
    const encodedQuery = encodeURIComponent(query);
    const response = await fetch(`https://en.wikipedia.org/wiki/${encodedQuery}`);
    if (!response.ok) throw new Error('页面不存在');
    
    const html = await response.text();
    const doc = new DOMParser().parseFromString(html, 'text/html');
    
    const title = doc.querySelector("h1#firstHeading")?.textContent || "";
    const paragraphs = Array.from(doc.querySelectorAll("div.mw-content-ltr p:not(.mw-empty-elt)"))
      .map(p => p.textContent)
      .join("\n\n");
    
    data = { heading: title, paragraphs };
  } catch (err) {
    data = { heading: "错误", paragraphs: err.message };
  }
}
---

<div class="border p-4">
  <h1>{data.heading}</h1>
  <div class="mt-4 whitespace-pre-line">{data.paragraphs}</div>
</div>

<script client:load>
  import { createStore } from 'astro:store';
  const store = createStore({ query: "" });

  // 监听query变化,重新渲染组件内容
  store.subscribe((state) => {
    if (!state.query) return;
    const container = document.currentScript?.closest('.border');
    if (!container) return;

    // 重新获取数据并更新DOM
    (async () => {
      try {
        const encodedQuery = encodeURIComponent(state.query);
        const response = await fetch(`https://en.wikipedia.org/wiki/${encodedQuery}`);
        if (!response.ok) throw new Error('页面不存在');
        
        const html = await response.text();
        const doc = new DOMParser().parseFromString(html, 'text/html');
        
        const title = doc.querySelector("h1#firstHeading")?.textContent || "";
        const paragraphs = Array.from(doc.querySelectorAll("div.mw-content-ltr p:not(.mw-empty-elt)"))
          .map(p => p.textContent)
          .join("\n\n");
        
        container.innerHTML = `
          <h1>${title}</h1>
          <div class="mt-4 whitespace-pre-line">${paragraphs}</div>
        `;
      } catch (err) {
        container.innerHTML = `<h1>错误</h1><div class="mt-4">${err.message}</div>`;
      }
    })();
  });
</script>

修改Index.astro使用状态管理

---
import Wiki from "../components/Wiki.astro";
---

<title>维基百科搜索</title>
<body class="m-5">
  <h1 class="text-3xl text-accent">输入关键词进行搜索:</h1>
  <form id="search-form">
    <input
      id="query"
      type="text"
      name="query"
      class="rounded border-b-[3px] border shadow-md px-3 py-2 w-96 mt-6 focus:outline-none focus:ring focus:ring-accent focus:ring-opacity-30 text-text"
      required
      placeholder="例如:George Washington"
    />
    <button type="submit" class="rounded border-2 px-3 py-2 ml-2">搜索</button>
  </form>
  <br />
  <Wiki query="" />

  <script>
    import { createStore } from 'astro:store';
    const store = createStore({ query: "" });

    const form = document.getElementById('search-form');
    form.addEventListener('submit', (e) => {
      e.preventDefault();
      const query = document.getElementById('query').value.trim();
      if (query) store.set({ query });
    });
  </script>
</body>

内容的提问来源于stack exchange,提问作者TsugikuniVi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 19:07:02