Svelte:动态语句中的代码不会更新组件

Svelte : code in dynamic statement doesn't update components

我有三个组件:应用程序、键盘和按键。

键盘组件根据具有这种结构的变量“键”加载多个按键组件:{ char: string, isPressed: boolean }[] 关键组件有一个布尔值 pressed 道具,除其他外还用于样式设置。

我希望能够从 App 组件重置所有这些 isPressed 属性,并在按下某些键时相应地更新视图。 使用 Vue.js 我可能使用了全局事件,在这里我尝试使用绑定的道具和动态语句。 所以我在键盘组件中有一个动态语句,它在我想要的时候执行,但只在你按下另一个键之前起作用。 有趣的是,当我使用“清除”按钮时它工作正常。

我在这里回复了:https://svelte.dev/repl/b7c11d9f0e494195884be0994387ba4c?version=3.35.0

应用程序

<main>
  <Keyboard on:pressed-change="{checkKeys}"
            bind:clear={clear} />
</main>

<script>
import Keyboard from './Keyboard.svelte';

let clear = false;
const text = 'AC'; 

function checkKeys({ detail: keys }) {
    console.log('check keys')
  if (keys.sort().join('') === text) {
        console.log('clear')
    clear = true;
  }
}
</script>

键盘

<main>
  <div>
    <section class="keys">
      {#each keys as { char, isPressed }, i}
        <Key char="{char}"
             bind:pressed={isPressed}
             on:key-press={handleKeyPress} />
      {/each}
    </section>

    <section>
      { pressed.join(' | ') }
    </section>
    <button on:click={clearKeyboard}>
      clear
    </button>
  </div>
</main>

<script>
import { createEventDispatcher } from 'svelte';
import Key from './Key.svelte';

export let clear;
let pressed = [];

const dispatch = createEventDispatcher();
    
let keys = [
  { char: "A", isPressed: false },
  { char: "B", isPressed: false },
  { char: "C", isPressed: false },
  { char: "D", isPressed: false },
  { char: "E", isPressed: false },
  { char: "F", isPressed: false },
  { char: "G", isPressed: false },
];

function handleKeyPress({ detail: char }) {
  const keyIndex = pressed.indexOf(char)
  if (keyIndex >= 0) {
    const tempArr = [...pressed];
    tempArr.splice(keyIndex, 1);
    pressed = tempArr;
    return;
  }
  pressed = [...pressed, char];
}

function clearKeyboard() {
  console.log('clear keyboard');
  keys = keys.map(key => ({ ...key, isPressed: false }));
  pressed = [];
}

$: if (clear) {
  clearKeyboard()
  clear = false;
}
$: dispatch('pressed-change', pressed);
</script>

密钥

<div class="key"
     class:pressed
     on:click="{ press }">
  { char }
</div>

<script>
import { createEventDispatcher } from 'svelte';

export let char;
export let pressed;

const dispatch = createEventDispatcher();

function press() {
  pressed = !pressed;
  dispatch('key-press', char);
}
</script>

感谢您的帮助。

您遇到的问题是由某处的某些竞争条件引起的。

Svelte 不会完全 'instantaneously' 更新 DOM 而是安排分批更改。您的代码中发生的事情是设置要按下的键的代码与类似的信号冲突 'unsetting' 它。

您可以通过使用 await tick() 告诉 Svelte 明确等待,直到 应用当前计划的更改(打勾是 Svelte 呈现到 DOM)

import { tick } from 'svelte'

async function clearKeyboard() {
  await tick()
  console.log('clear keyboard');
  keys = keys.map(key => ({ ...key, isPressed: false }));
  pressed = [];
}

在上面的代码中,首先应用当前的更改,然后将键全部设置为 false。

如果在混音中添加延迟,您可以通过在 await tick()

之后添加以下行来可视化发生的情况
await new Promise(res => setTimeout(res, 2000)) // will pause execution for two seconds