hgl*_*hgl 10 svelte svelte-store
Svelte 官方教程在其文档中使用了如此复杂的对象<svelte:self>
let root = [
{
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' }
];
Run Code Online (Sandbox Code Playgroud)
如果这个对象需要反应并放置在商店内,应该怎么做?应该将树包装为单个存储,还是每个文件和文件夹都是它自己的存储并相应地嵌套存储?
在这两种情况下,似乎每当顶级属性发生更改时(svelte store 认为来自对象的更新总是新鲜的),整个树都会被检查是否有变化?
rix*_*ixo 16
一些事情要知道...
$商店的前缀符号也用于为可写商店分配一个新值:
<script>
import { writable } from 'svelte/store'
const x = writable(0)
const onClick = () => {
$x = $x + 1
}
</script>
<button on:click={onClick}>+</button>
<span>{$x}</span>
Run Code Online (Sandbox Code Playgroud)
这也适用于写入对象的单个道具或数组中的单个项目:
<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>
Run Code Online (Sandbox Code Playgroud)
从父组件,您可以将变量绑定到子组件的 prop:
Child.svelte
<script>
export let value
</script>
<input bind:value />
Run Code Online (Sandbox Code Playgroud)
App.svelte
<script>
import Child from './Child.svelte'
let value = ''
$: console.log(value)
</script>
<Child bind:value />
Run Code Online (Sandbox Code Playgroud)
注意:绑定仅在它是相同的变量时才有效。也就是说,您不能将绑定变量放在中间变量中,并让 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>
Run Code Online (Sandbox Code Playgroud)
因此,知道所有这些,如果您将源数据放在可写存储中并且您对 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' },
])
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
但是请注意,这可能不是最有效的解决方案。
原因是对 store 任何部分的任何更改都将被检测为对整个 store 的更改,因此 Svelte 必须将更改传播并重新验证到每个消费者(组件)或此数据。我们不一定在谈论一些繁重的处理,因为 Svelte 仍然知道数据图,并且会通过非常便宜和有针对性的if测试很早就使大部分传播短路。但是,处理的复杂性仍然会随着存储中对象的大小线性增长(尽管缓慢)。
在某些情况下,数据可能非常大或其他(可能允许延迟获取嵌套节点?),您可能想要详细说明上述示例中演示的技术。例如,您可以通过将数据中的递归节点(即files上面示例中的prop)包装在一个可写存储中来限制处理更改的算法复杂性(成本)。是的,那将是商店中的商店(高阶商店?)。将其连接在一起肯定会有些微妙,但理论上这将为您提供近乎无限的可扩展性,因为每个更改只会传播到受影响节点的兄弟节点,而不是整个树。
| 归档时间: |
|
| 查看次数: |
2047 次 |
| 最近记录: |