如何在 Svelte 中使用 splice 后更新数组?

33

我正在学习Svelte,并在文档中看到数组需要重新分配才能更新组件或页面。为此,他们设计了一种更加惯用的解决方案。而不是编写以下代码:

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

您可以写成:

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

好的,有道理。但文档上说:

您可以使用类似的模式来替换 pop、shift、unshift 和 splice。

可是怎么做呢?我不知道如何从数组中 删除 元素。更重要的是,我该如何更符合惯用语的方式来编写以下代码?

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

Svelte的响应性是通过赋值触发的。因此,push、pop、slice等方法不起作用。请在赋值时使用"="。 - Tal
7个回答

32

你可以使用filter数组方法来创建一个新的数组,该数组不包含元素'hello'

messages = messages.filter(m => m !== 'hello');

2
哦,这是一个优雅的解决方案!我应该更好地学习所有的JS数组函数,以了解可能性。但是没错,这会起作用。谢谢! :) - Mielipuoli
在Chrome中,filter()函数的速度也快了大约15%:https://jsbench.me/5uljggpmmx/1 - thdoan

21

如前所述,Svelte的反应性是由赋值触发的。当前的Svelte教程使用了JavaScript(ES6)的扩展语法(三个点)将下一个更高的数添加到数组中,提供了比使用push的冗余赋值更惯用的解决方案:

function pushNumber() {     
    numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}

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

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
}   

Spread只是其中一种方法。不使用pop/push等的目的是为了鼓励不可变性。因此,任何删除操作都可以使用filter来实现。


1
这是一个更经典的答案,虽然被采纳的答案完全回答了问题,但它更为详尽,并涉及到了 Svelte 的惯用用法... - ortonomy

7

这里有几件事情需要考虑。

给出以下代码:

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

这里的操作是:
  1. 在数组中查找字符串 "hello"第一个 出现位置
  2. 根据找到的索引,从数组中删除该元素。

这里的假设是 "hello" 必须存在,否则代码将会删除数组的 最后一个 元素(因为 indexOf 返回 -1)。

因此,原始数组被改变了:这取决于上下文,有时候直接修改原数组可能更好,而不是将整个数组复制到新数组中;否则,通常最好避免对数组进行这样的修改。

所以。如果你想要确切地实现这种行为,可能这是你能得到的最佳代码。例如,看看filter的示例:

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

这里的操作是:
  1. 过滤掉所有等于"hello"的元素
  2. 返回一个新的不包含这些元素的数组
因此,它与原始代码有很大的不同:首先,它总是循环整个数组。如果您有数千个元素,即使在第二个索引处只有一个"hello",它也会遍历所有元素。也许这是您想要的,也可能不是。如果元素是唯一的,例如id,则可能希望在找到它后停止。 其次,它返回一个新数组。通常情况下,这通常比改变数组更好,但在某些情况下,将其改变而不是创建新数组可能更可取。
因此,如果您想要改变原始数组,最好坚持使用原始代码。
如果您不关心(例如push的示例),我认为在svelte的开发人员的意图中,您的代码大致转化为:
let i = messages.indexOf("hello"); 
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];

(仍然假设有一个“hello”消息,并且您只对第一次出现感兴趣。)
很遗憾,JS没有更好的语法来处理切片。

3

如果你想要知道,filter 也可以用来使用给定的 index 移除元素:

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

1
你可以对数组执行常规的pushpop或`splice`操作。
但是,由于Svelte的响应性是通过赋值触发的,因此使用类似push和splice的数组方法不会自动引起更新。
根据JavaScript中所有关于不可变数组和对象的内容,您可以这样做...
let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];

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

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

请注意spliceslice之间的区别。

splice()方法可向数组添加/删除项目,并返回已删除的项目。 注意:此方法更改原始数组。 语法:array.splice(start, deleteCount, itemstoAdd, addThisToo);

但是

slice()方法将所选元素作为新的数组对象返回。 slice()方法从给定的开始参数选择元素,并以给定的结束参数结束,但不包括它。 注意:原始数组不会被更改。

换句话说,

它将数组的一部分浅复制到一个新的数组对象中,该部分从begin到end(不包括end)进行选择。原始数组不会被修改。语法:array.slice(start, end);


0

-2
将拼接的数组展开并重新分配给自身 ;)
messages = [...messages.splice(messages.indexOf('hello'), 1)];

目标是让Svelte检测到数组messages(组件属性或Svelte存储中的变量)已更改。这就是为什么数组messages必须使用letvar关键字声明,而不是const。这样,您可以重新分配它。重新分配操作本身足以使Svelte检测到数组已更改。

也许,仅通过这样做也可以:

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

5
这不准确,splice方法会直接修改数组并返回被删除的元素。请参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice - Félix Adriyel Gagnon-Grenier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接