问题描述
我正在使用 sapper
构建一个站点,该站点对单个博客条目使用 [slug].svelte
约定。博客内容来自(模拟)数据库,包含 html。
html 包含在底部,如下所示:
...
<div class="content">
{@html post.html}
</div>
...
一切都很好,它呈现的 html 只是桃色。
但是,请考虑以下事项:
import AComponent from '../../components/AComponent.svelte'
而且,在 {@html post.html}
中呈现的包含的 html 中:
<p>yada yada yada</p>
<AComponent prop="data" />
<p>More yada yada yada...</p>
AComponent
不会被实例化或调用。
有什么办法可以做到这一点吗?还是我试图做一些不可能的事情?
(我知道该组件没问题 - 它已在另一个具有完整 html 的文件中进行了测试。)
谢谢
解决方法
我认为仅使用 @html
指令是不可能的。
来自the docs:
表达式应该是有效的独立 HTML — {@html "<div>"}content{@html "</div>"}
将不起作用,因为不是有效的 HTML。
由于 <AComponent prop="data" />
是一个 Svelte 组件而不是独立的 HTML,因此在使用 @html
时它不会实例化自身。
但是,您可以组合使用 <svelte:component>
来从字符串内容动态呈现组件的解决方案。
这是 Svelte REPL 中的概念的快速证明。有一堆边缘情况未被发现,但它表明这是可能的。我也粘贴了下面的代码。
<script>
import ComponentA from './ComponentA.svelte';
import ComponentB from './ComponentB.svelte';
let raw = `<p>yada yada yada</p>
<ComponentA name="testing" />
<p>More yada yada yada...</p>`;
$: rawLines = raw.split('\n');
function getComponent(line) {
const componentName = line.split(' ')[0].substring(1);
switch (componentName) {
case 'ComponentA':
return ComponentA;
case 'ComponentB':
return ComponentB;
}
return null;
}
function getComponentProps(line) {
const props = line.split(' ').slice(1,-1);
const kvPairs = props.map(p => p.replaceAll('"','').split('='));
return Object.fromEntries(kvPairs);
}
</script>
<textarea bind:value={raw} />
{#each rawLines as line}
{#if line[1] === line[1].toUpperCase()}
<svelte:component this={getComponent(line)} {...getComponentProps(line)}></svelte:component>
{:else}
{@html line}
{/if}
{/each}