如何在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
相关产品推荐
相关产品推荐

