首页 > 解决方案 > Sapper/Svelte - 需要 @html 包含的内容来调用组件

问题描述

我正在构建一个使用sapper单个[slug].svelte博客条目约定的网站。博客内容来自(模拟)数据库,并包含 html。

html 包含在底部,如下所示:

...
<div class="content">
  {@html post.html}
</div>
...

一切都很好,它使 html 变得很漂亮。

但是,请考虑以下事项:

[slug].svelte文件中:

import AComponent from '../../components/AComponent.svelte'

并且,在包含的 html 内呈现{@html post.html}

<p>yada yada yada</p>
<AComponent prop="data" />
<p>More yada yada yada...</p>

AComponent不会被实例化或调用。

有没有办法做到这一点?还是我试图做一些不可能的事情?

(我知道组件没问题 - 它已经在另一个具有完整 html 的文件中进行了测试。)

谢谢

标签: javascriptsveltesapper

解决方案


我不相信@html仅凭指令就可以做到这一点。

文档

表达式应该是有效的独立 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}

推荐阅读