为什么我的 svelte {#each} 块不是反应式的?

Why isn't my svelte {#each} block reactive?

所以基本上我在玩 Svelte 试图启动一个快速应用程序,应用程序的细节并不重要但基本上它托管了一堆嵌入式网站。请参阅此处的示例并了解可复制性:

https://svelte.dev/repl/6f3484554ef8489b9a5960487a0a1f95?version=3.47.0

我的问题是,当我向站点列表添加新的 url & 标题时,创建嵌入视图的 {#each} 块不会更新以反映列表的新状态,即使列表在控制台输出中明显更新。这与范围有关,还是不触发组件重新分配 prop 的反应性的 Svelte 问题?

更新: 一些网站不允许嵌入,因此使用 https://wikipedia.org 作为安全的测试。


如果您将站点列表中的 hard-coded url 替换为 wiki 地址,它应该可以正常工作。我基本上希望在 {#each} 块创建一个新的 SiteView 组件

时弹出一个新的 window

如果您想从另一个组件更改值,您需要 bind 属性,否则关系仅为 one-way(从父组件到子组件)。

<InputBar bind:sites {site}/>

您的代码有几处错误,首先是您没有将对 sites 数组所做的更改传播回主应用程序,您应该使用 bind: 来保留两个阵列同步。

<InputBar bind:sites {site} />

第二个是您在添加新站点时修改对象,然后将该对象添加到数组中,这将始终是 相同的 对象,因此如果您更改它先前添加的站点也将更改。您可以通过将新对象散布到数组中来解决此问题:

function add() { sites = sites.concat({...site}); console.log(sites)}
// or alternatively
function add() { sites = [...sites, {...site}]; console.log(sites); }

也就是说,该应用程序不是很“Svelte”,因为它混合了职责并将数据公开给不需要该数据的组件。例如,为什么输入栏需要了解当前站点?如果输入栏只是一个输入栏,那就更好了。当用户单击 'add' 时,它会引发一个事件 'something has been added' 并重置字段。然后父级负责将它添加到数组中。这将提供更灵活的解决方案。如果你这样做,你会发现也没有理由在顶层有一个 'site' 变量(甚至根本没有那个对象,你可以只有两个字段)


<script>
    import { createEventDispatcher } from 'svelte'
    let url = ''
    let title = ''
    const dispatch = createEventDispatcher()
    function add() {
        dispatch('add', { url, title })
        url = ''
        title = ''
    }
</script>
<div class="rounded">
    <p>Enter a site to stream:</p>
    <input type="text"  placeholder="www.example.com" bind:value={url}>
    <br>
    <input type="text" placeholder="example" bind:value={title}>
    <button on:click={add}>add</button>
</div>
<InputBar on:add={(ev) => sites = [...sites, ev.detail]} />

最后一点,要将内容添加到 html 的头部,请改用 <svelte:head>