如何在Astro项目中使用<Code>组件,规避‘Astro组件无法直接函数调用’报错?
问题原因与解决方法
错误原因
Astro官方的Code组件属于Astro专属组件,只能在.astro后缀的文件里直接渲染,不能在React(.jsx/.tsx)这类框架组件里通过JSX语法直接调用。这是Astro组件的运行机制决定的——Astro组件是在服务端编译处理的,和React组件的客户端/混合渲染逻辑不兼容,所以会抛出"Astro components cannot be rendered directly via function call"错误。
解决办法
方法一:将Code组件移到Astro文件中使用
如果代码展示逻辑不需要完全在React组件里处理,可以把Code组件直接放在Astro文件里,把React组件需要传递的代码内容通过props传过来:
- 修改React组件(CodeViewer.jsx),把要展示的代码作为props接收:
import React, { useState } from 'react'; function CodeViewer({ onCodeChange }) { const [codeToPresent, setCodeToPresent] = useState('const foo = \'bar\';'); // 若需修改代码并同步给Astro文件,可通过回调函数传递 const handleChange = (e) => { setCodeToPresent(e.target.value); onCodeChange(e.target.value); }; return ( <textarea value={codeToPresent} onChange={handleChange} /> ) } export default CodeViewer;
- 在Astro文件里同时引入React组件和Code组件,处理代码展示:
--- import Layout from '../layouts/Layout.astro'; import CodeViewer from '../components/CodeViewer.jsx'; import { Code } from 'astro/components'; import { useState } from 'react'; // 用React的useState接收React组件传递的代码 const [currentCode, setCurrentCode] = useState('const foo = \'bar\';'); --- <Layout> <CodeViewer onCodeChange={setCurrentCode} /> <Code code={currentCode} lang="js" /> </Layout>
方法二:在React组件中使用兼容的代码高亮库
如果必须在React组件内实现代码高亮,可以用支持React的代码高亮工具,比如react-syntax-highlighter,它和React的渲染逻辑完全兼容:
- 安装依赖:
npm install react-syntax-highlighter
- 在React组件中使用:
import React, { useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism'; function CodeViewer() { const [codeToPresent, setCodeToPresent] = useState('const foo = \'bar\';'); return ( <SyntaxHighlighter language="javascript" style={tomorrow}> {codeToPresent} </SyntaxHighlighter> ) } export default CodeViewer;
方法三:直接使用Astro Code组件的底层依赖shiki
Astro的Code组件底层用的是shiki,你也可以直接在React里集成shiki来实现相同的高亮效果:
- 安装shiki:
npm install shiki
- 在React组件中使用(注意shiki是服务端优先的库,客户端使用需要处理异步加载):
import React, { useState, useEffect, useRef } from 'react'; import { getHighlighter } from 'shiki'; function CodeViewer() { const [codeToPresent, setCodeToPresent] = useState('const foo = \'bar\';'); const codeRef = useRef(null); useEffect(() => { async function highlightCode() { const highlighter = await getHighlighter({ theme: 'nord', langs: ['javascript'] }); if (codeRef.current) { codeRef.current.innerHTML = highlighter.codeToHtml(codeToPresent, { lang: 'js' }); } } highlightCode(); }, [codeToPresent]); return ( <div> <textarea value={codeToPresent} onChange={(e) => setCodeToPresent(e.target.value)} /> <div ref={codeRef} /> </div> ) } export default CodeViewer;
内容的提问来源于stack exchange,提问作者Itay k
相关产品推荐
相关产品推荐

