Svelox 是使对象的 API 兼容 Svelte 的反应式特性,例如数组的 push
、splice
和 pop
等 API,调用它们之后,你无需再次使用赋值语句用来触发状态的更新。
Svelte 依赖赋值来生成状态更新的代码的,具体参看 Svelte 官网上的这个例子。
svelox 没有魔法,使用它也需要编写一次类似 arr = arr
之类的奇怪赋值语句,因为没有任何方式可以通过编程实现这条语句的相同效果,它必须由 Svelte 编译器生成,且必须是在 .svelte
组件文件内编写这一行代码。
svelox 仅仅是让你只需写一次 arr = arr
赋值语句而已。
你首先需要编写一个 updater
函数,它的函数体一般只需要一句类似 arr = arr
的赋值语句即可。
svelox 通过劫持对象的 API(由你决定劫持哪些 API)来实现自动调用 updater
,以此获得状态更新能力。
使用 npm install
(或 pnpm install
、yarn
) 安装 svelox:
npm install --save-dev svelox
svelox 仅导出一个函数 sx
,它的构造方法说明如下:
function sx(target, updater, methods)
// 调用示例,target 是数组类型,可忽略提供 methods
let arr = sx([1, 2, 3], (n) => arr = n)
参数说明:
target
是你的对象,可以是数组也可以是普通对象updater
一般直接写为(n) => arr = n
即可methods
需要劫持的函数名称列表,如['push', 'splice']
,如果target
是数组可忽略,默认会将改变数组自身的所有API都劫持
返回值 arr
实际上是一个 Proxy
对象。
import
这个 sx
函数,下方是一个简单的示例:
<script lang="ts">
import { sx, type Reactable } from "svelox"
let numbers: Reactable<number[]> = sx([1, 2, 3], (n) => (numbers = n))
let value = "";
</script>
<!-- svelte-ignore a11y-autofocus -->
<input autofocus bind:value />
<button disabled={value == ""} on:click={() => numbers.push(parseInt(value, 10))}>add number</button>
<button on:click={() => numbers.reset([4, 5, 6])}>reset to [4, 5, 6]</button>
<h4>numbers:</h4>
<ul>
{#each numbers as item, index}
<li>
<input type="number" bind:value={item} />
<button on:click={() => numbers.remove(index)}>×</button>
</li>
{/each}
</ul>
<button on:click={() => numbers.clear()}>clean all</button>
<h4>sum = {numbers.reduce((s, c) => (s += c), 0)}</h4>
点击此处在 Svelte REPL 查看该示例。
你可以看到,使用 push
后并不需要再写一句 numbers = numbers
之类的赋值语句。
数组会默认劫持下列函数,你无需提供:
copyWithin
fill
pop
push
reverse
shift
sort
splice
unshift
如你所见,这些 API 都会改变数组本身。
- 调用
sx(...)
函数后,原对象已被Proxy
包裹作为返回值返回。 - 所有对象均会添加一个
reset
方法,用于重置对象的值。 - 如果对象为
数组
类型,则另外增加两个方法:remove(index)
用于更便捷地删除指定索引的元素,等同代码是splice(index, 1)
,以及clear()
清空数组。 - 你
不
应该让指定在methods
内的方法相互调用,这可能会引起死循环。或者在插值表达式中不应该使用添加在methods
中的方法,也会引起死循环。例如,下方示例如果将数组的reduce()
方法添加到methods
列表中,这将引起死循环:
<script>
import { sx } from "svelox"
let numbers = sx([1, 2, 3], (n) => (numbers = n), ['reduce'])
</script>
<h4>sum = {numbers.reduce((s, c) => (s += c), 0)}</h4>
上述代码中,已在插值表达式中使用 numbers.reduce
来计算数组总和,但是同时将 reduce
方法添加到 methods
列表中,这将导致 numbers.reduce
被调用时,自动执行一次 updater
方法,也即 (n) => (numbers = n)
,这使得 numbers
被视为脏值,让下方的 numbers.reduce
再次执行,如此重复。
-
如果你想将旧有代码迁移到 Svelte,例如
Vue
,因为它们默认都使用了push
来向数组添加新项,这种情况下,你难以一个一个地去查找并为其编写一句重新赋值语句。 这个时候 svelox 可以提供有效的帮助,节省大量的时间。 -
有时候确实使用 Array 原生的 APIs 时的代码会比重新赋值的代码要短,例如移除一个元素
arr.splice(index, 1)
,要比arr = [...arr.slice(0, index), ...arr.slice(index + 1)]
要短,认同吗?
MIT License (MIT)
Copyright (c) 2022-present, [email protected]