Svelte 中是否有 "nicer" 方法从导出的 prop 声明和更新多个变量?

Is there a "nicer" way in Svelte to declare and update multiple variables from an exported prop?

背景

我正在为我的公司开展一个项目。

我们决定在这个项目中使用 Svelte。我们正在监视来自此应用程序中微服务的信息。

我构建了一个组件,它从名为“值”的对象中获取信息,并使用销毁符号声明它的道具。该值将被更新。在每次更新时,“值”的道具都用于对它们进行计算,并直接在模板中渲染结果或道具。

这里是源码(省略不必要的部分):

<script>

let state = "default";
let dayDiff = 0;
let dateOutput;
let valuesAvailable;

export let value;

let {name, finished, started, running, lastRun, completion, messages} = value;
$: {
 ;({name, finished, started, running, lastRun, completion, messages} = value)

 valuesAvailable = Object.keys(value).length > 0;
 if(selectedItemStore === name) {
   selectedItemStore.set(value)
 }

 if (valuesAvailable) {
   if (completion) {
     completion = Number(value.completion.slice(0, -1));
   } else {
     completion = 0;
   }
   const messageArray = Object.entries(messages);
   if (running) {
     state = 'running';
   } else {
     if (completion === 100) {
       state = 'done'
     }
     if (messageArray.length > 0) {
       state = "info";
     }
   }
   if (finished) {
     dateOutput = Utils.getRelOrAbsoluteDate(finished)
   } else if (running && started) {
     dateOutput = "running...";
   } else {
     state = "never-run";
     dateOutput = "Did not run before"
   }
   if (completion < 100 && !running && started) {
     state = 'error';
   }
 }
}
</script>

<div class={"item " + state + (selectedItemStore === value.name ? " selected" : "")} on:click={toggleSelection}>
 
 <span class="name">{name}</span>
 <iron-icon icon={iconMappings[state]}></iron-icon>
 {#if state!=='never-run'}
     <div class="bar">
         <CompletionIndicator value={completion}/>
     </div>
 {/if}
 <span class="date">
     {dateOutput}
 </span>
</div>

问题

这些行有问题:

<script>
    export let value;

    let {name, finished, started, running, lastRun, completion, messages} = value;
    $: {
        ;({name, finished, started, running, lastRun, completion, messages} = value)
    
        (...)
    }
</script>

$: { ... } 中的代码在每次“值”改变时执行。我这样做是因为必须在每次渲染时重新进行“计算”。在“反应函数”之外,实际值被声明为在模板中使用。它们必须在反应函数范围之外声明,这样它们才能在实际的 Svelte 模板中使用。

结论

这是可行的,但首先不容易阅读。我还没有找到使它更具可读性和工作性的解决方案。 单行代码或另一种解决此问题的方法会很好。 你有什么想法吗?

编辑:

我们已经找到了一个很好的解决方案,在这个组件之外使用 {#key value} 语法标记为已接受的答案。 无论如何,就性能而言,仅使用带有反应式声明的 解决方案会更聪明。

如果道具太多,我更喜欢使用商店,但在你的情况下,为什么不检查父对象本身是否有任何变化:

<script>
    export let value;
    $: if (value) {
        // do your magic
    }
</script>

并且您可以在需要时直接使用value.porp

您可以使用 #key 块重新呈现您需要动态数据的组件。如果组件内部有转换,这可能是不可取的,因为它们需要一些变通方法 运行 正确地位于 #key 块内,但它可能对您的用例有所帮助。

这是一个更简化的示例,其中父项中的数据发生变化并且组件(具有解构的对象)重新呈现以匹配父项中的状态,而无需任何反应语句。这里有一个 REPL 可以试试。

App.svelte

<script>
    import Component from './Component.svelte'
    let value = {name: 'Creed Bratton', age: 30}
</script>

<input bind:value={value.name} />
<input type="number" bind:value={value.age} />

{#key value}
    <Component {value} />
{/key}

Component.svelte

<script>
    export let value;
    let {name, age} = value
</script>

<div>
    <p>
        {name} - {age}
    </p>
    {#if age > 30}
        <p>Wow, that is old</p>
    {/if}
</div>

将它分成两个响应式语句,如下所示:

export let value;

$: ({name, finished, started, running, lastRun, completion, messages} = value);
$: {
    (...)
}