我正在学习Svelte,并在文档中读到需要重新分配数组才能更新组件或页面。为此,他们设计了一个更惯用的解决方案。代替书写:
messages.push('hello');
messages = messages;
你可以改为写:
messages = [...messages, 'hello'];
好吧,有道理。但随后文件显示:
您可以使用类似的模式来替换pop、shift、unshift和splice。
但是怎么做呢?我看不出如何从数组中删除项。更重要的是,我怎么能写得更地道呢?
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
例如,您可以使用filter array方法创建一个没有元素'hello'
:的新数组
messages = messages.filter(m => m !== 'hello');
如前所述,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
}
不过,扩散只是一种方法。不使用pop/push等的目的是鼓励不变性。例如,任何移除都可能只是一个过滤器。
这里有几件事需要考虑。给定此代码:
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
这里发生的是:
- 查找数组中字符串
"hello"
的第一次出现 - 根据找到的索引从数组中删除此类元素
这里的假设是"hello"
需要存在,否则can将从数组中删除最后一个项(因为indexOf
返回-1
)。
因此,原始数组会发生变化:这取决于上下文,有时可能更可取,而不是将整个数组复制到新数组中;否则,避免这种突变通常是更好的做法。
所以。如果你想让这种行为精确地,这可能是你能拥有的最好的代码。例如,以过滤器为例:
messages = messages.filter(message => message !== "hello")
这里发生的是:
- 过滤掉任何等于
"hello"
的元素 - 返回不包含此类元素的新数组
所以它与原始代码大不相同:首先,它总是循环整个数组。如果您有数千个元素,即使在第二个索引处只有一个"hello"
,它也会迭代所有元素。也许这是你想要的,也许不是。如果元素是唯一的,比如id,也许你想在找到它后停止。其次,它返回一个新数组。同样,这通常是比变异数组更好的做法,但在某些情况下,最好变异数组,而不是创建新的数组。
因此,如果您想对原始数组进行变异,那么最好还是使用原始代码。
相反,如果你不在乎(比如push
的例子),我相信在svelte的开发人员的意图中,你的代码将大致翻译为:
let i = messages.indexOf("hello");
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];
(仍然假设有一条"hello"
消息,并且您只对第一次出现感兴趣)。
遗憾的是,JS没有更好的语法来处理切片。
如果您正在漫游,filter
也可以用于使用给定的index
:删除元素
let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']
您可以在阵列上执行常见的push
和pop
或拼接
但由于Svelte的反应性是由分配触发的,使用推送和拼接等数组方法不会自动导致更新。
根据JavaScript中关于不可变数组和对象的所有内容,您可以这样做。。。
let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];
const indexOfHello = messages.indexOf('hello');
messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];
注意拼接和切片之间的差异
splice()方法向数组添加项/从数组中删除项,并返回删除的项目注意:此方法会更改原始数组。语法:
array.splice(start, deleteCount, itemstoAdd, addThisToo);
但是
slice()方法将数组中选定的元素作为新的数组对象返回。slice()方法选择从给定的开始参数开始的元素,并在给定的结束参数结束(但不包括)。注意:原始数组不会更改。
按顺序排列的单词
它将数组的一部分的浅拷贝返回到新数组中从开始到结束选择的对象(不包括结束)。原件数组将不会被修改。语法:
array.slice(start, end);
您可以尝试以下操作:https://svelte.dev/repl/0dedb37665014ba99e05415a6107bc21?version=3.53.1
使用一个名为svelox的库。它允许您在没有重新分配语句的情况下使用Array本机api(push/splice…等)。
展开拼接的数组,将其重新分配给自己;)
messages = [...messages.splice(messages.indexOf('hello'), 1)];
目标是使Svelte检测到阵列messages
(组件的属性或Svelte存储中的变量)已更改。这就是为什么数组messages
必须用let
或var
关键字而不是const
声明的原因。通过这种方式,您可以重新分配它。重新分配操作本身就足以让Svelte检测到阵列已经更改。
也许甚至,简单地这样做也有效:
messages = messages.splice(messages.indexOf('hello'), 1);