有几件事要知道...
商店的$
前缀符号也可以为可写商店分配一个新值:
<script>
import { writable } from 'svelte/store'
const x = writable(0)
const onClick = () => {
$x = $x + 1
}
</script>
<button on:click={onClick}>+</button>
<span>{$x}</span>
这也适用于写入对象的单个道具或数组中的单个项目:
<script>
import { writable } from 'svelte/store'
const x = writable({
count: 0,
})
const onClick = () => {
$x.count = $x.count + 1
}
</script>
<button on:click={onClick}>+</button>
<span>{$x.count}</span>
从父组件中,您可以将变量绑定到子组件的 prop :
Child.svelte
<script>
export let value
</script>
<input bind:value />
App.svelte
<script>
import Child from './Child.svelte'
let value = ''
$: console.log(value)
</script>
<Child bind:value />
注意:绑定仅在它是同一个变量时才有效。也就是说,您不能将绑定变量放在中间变量中,并让 Svelte 继续跟踪此绑定。Svelte 确实会一直跟踪对象的各个道具(只要它们是从最初绑定的变量中引用的——使用点表示法),以及数组项,特别是在{#each}
循环中:
<script>
import { writable } from 'svelte/store'
const x = writable({
count: 0,
})
const y = writable([
{ count: 0 },
{ count: 1 },
])
const onClick = () => {
$x.count = $x.count + 1
}
</script>
<button on:click={onClick}>+</button>
<span>{$x.count}</span>
<hr />
{#each $y as item, i}
<div>
<button on:click={() => item.count++}>$y[{i}]: +</button>
</div>
{/each}
<pre>{JSON.stringify($y)}</pre>
因此,知道这一切后,如果您将源数据放在可写存储中,并且您的 2 路绑定很精确,那么您最终可以得到一个非常便宜的解决方案来解决您的问题......(参见REPL)
stores.js
import { readable, writable, derived } from 'svelte/store'
// a big writable store
export const root = writable([
{
type: 'folder',
name: 'Important work stuff',
files: [{ type: 'file', name: 'quarterly-results.xlsx' }],
},
{
type: 'folder',
name: 'Animal GIFs',
files: [
{
type: 'folder',
name: 'Dogs',
files: [
{ type: 'file', name: 'treadmill.gif' },
{ type: 'file', name: 'rope-jumping.gif' },
],
},
{
type: 'folder',
name: 'Goats',
files: [
{ type: 'file', name: 'parkour.gif' },
{ type: 'file', name: 'rampage.gif' },
],
},
{ type: 'file', name: 'cat-roomba.gif' },
{ type: 'file', name: 'duck-shuffle.gif' },
{ type: 'file', name: 'monkey-on-a-pig.gif' },
],
},
{ type: 'file', name: 'TODO.md' },
])
App.svelte
<script>
import { root } from './stores.js'
import Folder from './Folder.svelte'
$: console.log($root)
</script>
<div class="hbox">
<div>
<!-- NOTE binding to the store itself: bind=files={root} -->
<Folder readonly expanded bind:files={$root} file={{ name: 'Home' }} />
</div>
<pre>{JSON.stringify($root, null, 2)}</pre>
</div>
<style>
.hbox {
display: flex;
justify-content: space-around;
}
</style>
Folder.svelte
<script>
import File from './File.svelte'
export let readonly = false
export let expanded = false
export let file
export let files
function toggle() {
expanded = !expanded
}
</script>
{#if readonly}
<!-- NOTE bindings must keep referencing the "entry" variable
(here: `file.`) to be tracked -->
<span class:expanded on:click={toggle}>{file.name}</span>
{:else}
<label>
<span class:expanded on:click={toggle} />
<input bind:value={file.name} />
</label>
{/if}
{#if expanded}
<ul>
{#each files as file}
<li>
{#if file.type === 'folder'}
<!-- NOTE the intermediate variable created by the #each loop
(here: local `file` variable) preserves tracking, though -->
<svelte:self bind:file bind:files={file.files} />
{:else}
<File bind:file />
{/if}
</li>
{/each}
</ul>
{/if}
<style>
span {
padding: 0 0 0 1.5em;
background: url(tutorial/icons/folder.svg) 0 0.1em no-repeat;
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
min-height: 1em;
display: inline-block;
}
.expanded {
background-image: url(tutorial/icons/folder-open.svg);
}
ul {
padding: 0.2em 0 0 0.5em;
margin: 0 0 0 0.5em;
list-style: none;
border-left: 1px solid #eee;
}
li {
padding: 0.2em 0;
}
</style>
File.svelte
<script>
export let file
$: type = file.name.slice(file.name.lastIndexOf('.') + 1)
</script>
<label>
<span style="background-image: url(tutorial/icons/{type}.svg)" />
<input bind:value={file.name} />
</label>
<style>
span {
padding: 0 0 0 1.5em;
background: 0 0.1em no-repeat;
background-size: 1em 1em;
}
</style>
但是请注意,这可能不是最有效的解决方案。
原因是对商店任何部分的任何更改都将被检测为对整个商店的更改,因此 Svelte 必须将更改传播并重新验证到每个消费者(组件)或此数据。if
我们不一定要谈论一些繁重的处理,因为 Svelte 仍然知道数据图,并且会在很早的时候通过非常便宜且具有针对性的测试来短路大部分传播。但是,处理的复杂性仍然会随着存储中对象的大小线性增长(尽管速度很慢)。
在某些数据可能非常大或某些情况下(可能允许延迟获取嵌套节点?),您可能需要详细说明上述示例中演示的技术。例如,您可以通过将数据中的递归节点(即files
上例中的道具)包装在可写存储中来限制处理更改的算法复杂性(成本)。是的,那将是商店中的商店(高级商店?)。这肯定会有点微妙的连接在一起,但理论上这会给你带来近乎无限的可扩展性,因为每个更改只会传播到受影响节点的兄弟姐妹,而不是整个树。