如何在 Svelte 中拼接后更新数组?

How to update an array after splice in Svelte?

我正在学习 Svelte,并在文档中读到需要重新分配数组以便组件或页面对其进行更新。为此,他们设计了一个更惯用的解决方案。而不是写:

messages.push('hello');
messages = messages;

你可以这样写:

messages = [...messages, 'hello'];

好吧,有道理。但是随后文档说:

You can use similar patterns to replace pop, shift, unshift and splice.

但是怎么办?我看不出如何从数组中 删除 项。更重要的是,我怎样才能更地道地写出以下内容?

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

你可以,例如使用 filter 数组方法创建一个没有元素 'hello':

的新数组
messages = messages.filter(m => m !== 'hello');

展开拼接数组以将其重新分配给自己;)

messages = [...messages.splice(messages.indexOf('hello'), 1)];

目标是让 Svelte 检测到数组 messages(组件的 属性 或 Svelte 商店中的变量)已更改。这就是数组 messages 必须使用 letvar 关键字而不是 const 声明的原因。这样你就可以重新分配它。并且重新分配操作本身足以让 Svelte 检测到数组已更改。

也许甚至,简单地这样做也行得通:

messages = messages.splice(messages.indexOf('hello'), 1);

这里有几件事需要考虑。 鉴于此代码:

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

这里发生的事情是:

  1. 在数组
  2. 中查找字符串"hello"第一次出现
  3. 根据找到的索引从数组中删除此类元素。

这里的假设是 "hello" 需要 存在,否则可能会从数组中删除 last 项(因为 indexOf returns -1)。

原始数组因此是变异的:取决于上下文,有时可能比将整个数组复制到一个新数组更可取;否则通常最好避免这种突变。

所以。如果您希望完全具有这种行为,这可能是您可以拥有的最佳代码。例如,以filter为例:

messages = messages.filter(message => message !== "hello")

这里发生的事情是:

  1. 过滤掉任何元素等于"hello"
  2. Returns new 数组没有这样的元素

所以它与原始代码有很大不同:首先,它总是循环整个数组。如果您有数千个元素,即使您在第二个索引处只有一个 "hello" ,它也会始终迭代所有这些元素。也许这就是你想要的,也许不是。如果该元素是唯一的,例如 id,您可能希望在找到它后停止。 其次,它 returns 一个新数组。同样,这通常比改变数组更好,但在某些情况下,改变它而不是创建一个新数组更可取。

因此,如果您想改变原始数组,最好坚持使用原始代码。

相反,如果您不关心(例如 push 的示例),我相信按照 svelte 开发人员的意图,您的代码将大致翻译为:

let i = messages.indexOf("hello"); 
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];

(仍然假设有一条 "hello" 消息,并且您只对第一次出现感兴趣)。

不幸的是,JS 没有更好的语法来处理切片。

您可以在阵列上执行通常的 pushpop 或`拼接

But because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.

根据 All about Immutable Arrays and Objects in JavaScript 你可以这样做...

let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];

const indexOfHello = messages.indexOf('hello');

messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];

注意拼接切片

的区别

The splice() method adds/removes items to/from an array, and returns the removed item(s). Note: This method changes the original array. Syntax: array.splice(start, deleteCount, itemstoAdd, addThisToo);

但是

The slice() method returns the selected elements in an array, as a new array object. The slice() method selects the elements starting at the given start argument, and ends at, but does not include, the given end argument. Note: The original array will not be changed.

顺序的话

It return a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified. Syntax: array.slice(start, end);

如前所述,Svelte 的反应是由分配触发的。 current Svelte tutorial uses JavaScript's (ES6) spread syntax(三个点)将下一个更高的数字添加到数组中,提供比使用 push:

的冗余赋值更惯用的解决方案
function pushNumber() {     
    numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}

您可以使用扩展语法来替换 popshiftunshiftsplice,但在某些情况下它可能会增加操作的时间和复杂性:

function unshiftNumber() {  
    numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4
}

function popNumber() {
    numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3
}

function shiftNumber() {
    numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4
}

function spliceNumber() {
    numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)];// 0, 1, 2, 3
}   

不过,传播只是一种方法。不使用 pop/push 等的目的是鼓励不变性。因此,例如,任何删除都可以只是一个过滤器。

以防万一,filter 也可用于使用给定的 index:

删除元素
let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']