为什么我的 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>
。
所以基本上我在玩 Svelte 试图启动一个快速应用程序,应用程序的细节并不重要但基本上它托管了一堆嵌入式网站。请参阅此处的示例并了解可复制性:
https://svelte.dev/repl/6f3484554ef8489b9a5960487a0a1f95?version=3.47.0
我的问题是,当我向站点列表添加新的 url & 标题时,创建嵌入视图的 {#each}
块不会更新以反映列表的新状态,即使列表在控制台输出中明显更新。这与范围有关,还是不触发组件重新分配 prop 的反应性的 Svelte 问题?
更新: 一些网站不允许嵌入,因此使用 https://wikipedia.org 作为安全的测试。
如果您将站点列表中的 hard-coded url 替换为 wiki 地址,它应该可以正常工作。我基本上希望在 {#each}
块创建一个新的 SiteView
组件
如果您想从另一个组件更改值,您需要 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>
。