Svelte中的状态管理

作者:renzp94
时间:2021-03-25 03:56:56

状态管理一般用于全局共用的一些数据的管理。关于状态管理,在其他的框架中都默认没有提供,都是通过新的库来支持的。比如VuexRedux。但在Svelte中内置了状态管理,而且用法非常简单。在Svelte中关于状态管理的函数都封装在svelte/store模块中,此模块可以导出readablewritablederived的功能。

readable

通过readable创建的状态(数据),在外部不能直接修改,只能在其内部更改。第一个参数是初始值,可为nullundefined。第二个参数是一个函数,函数接受一个set参数(set是一个函数用于处理内部数据),如果返回一个函数,则在取消订阅时会被调用。

在线 REPL

<script>
    import { readable } from 'svelte/store';

    const double = (val) => val * 2;
    let count = readable(null, (set) => {
        let i = 1;
        set(i);
        const interval = setInterval(() => set(++i), 1000);
        return () => clearInterval(interval);
    });
</script>

<h1>{double($count)}</h1>

$count是一个语法糖,后续会说明

writable

通过writable创建的状态(数据),在外部可以通过update更新,set设置值,subscribe订阅状态的改变(subscribe返回一个函数用于取消订阅)。

在线 REPL

<script>
    import { onDestroy } from 'svelte';
    import { writable } from 'svelte/store';

    let count = writable(0);
    let countValue = 0;

    const unSubscribe = count.subscribe((val) => (countValue = val));
    const onSub = () => count.update((n) => n - 1);
    const onAdd = () => count.update((n) => n + 1);
    const onReset = () => count.set(0);

    onDestroy(unSubscribe);
</script>

<h1>countValue:{countValue}</h1>
<button on:click={onSub}>-</button>
<button on:click={onAdd}>+</button>
<button on:click={onReset}>reset</button>

上面的例子中,可以通过$count进行精简代码,当使用$count时可以自动订阅状态变化以及在组件销毁时取消状态的订阅,而且$count是响应式的,即:count.update(val => val + 1)可以写成$count += 1

在线 REPL

<script>
    import { writable } from 'svelte/store';

    let count = writable(0);

    const onSub = () => count.update((n) => n - 1);
    const onAdd = () => count.update((n) => n + 1);
    const onReset = () => count.set(0);
</script>

<h1>count:{$count}</h1>
<button on:click={onSub}>-</button>
<button on:click={onAdd}>+</button>
<button on:click={onReset}>reset</button>

derived

通过derived可以通过状态来派生出来另一个状态,当依赖的状态发生变化时则立即调用其回调。回调函数接受三个参数:第一个参数是依赖的状态。第二个参数是回调函数,回调函数第一个参数是是依赖的状态变化之后的值,第二个参数是set函数用于设置新的派生状态,此函数中可以进行异步操作。第三个参数是一个默认值,当回调函数是一个异步函数时,此值才有效。

此外,如果不使用set函数,可以直接返回一个值作为派生状态。但是如果有set函数,则需要用set函数来设置。

在线 REPL

<script>
    import { writable, derived } from 'svelte/store';

    let count = writable(0);
    let doubleCount = derived(count, ($val) => $val * 2);
    let doubleSetCount = derived(count, ($val, set) => set($val * 2));
    let asyncDoubleCount = derived(
        count,
        async ($val, set) => {
            const data = await new Promise((resolve) => {
                setTimeout(() => {
                    resolve($val * 4);
                }, 1000);
            });

            set(data);
        },
        'loading'
    );
    const onAdd = () => $count++;

    let time = writable(new Date());
    let timer = derived(time, ($time, set) => {
        const interval = setInterval(() => set(new Date()), 1000);

        return () => clearInterval(interval);
    });
</script>

<h1>count: {$count}</h1>
<h1>doubleCount: {$doubleCount}</h1>
<h1>doubleSetCount: {$doubleSetCount}</h1>
<h1>asyncDoubleCount: {$asyncDoubleCount}</h1>
<button on:click={onAdd}>+1</button>
<h1>timer: {$timer}</h1>

get

如果你仅仅是想获取状态值,可以通过get来获取,这个函数只是获取当前状态的值。

在线 REPL

<script>
    import { writable, get } from 'svelte/store';

    let count = writable(0);
    console.log(get(count));

    const onAdd = () => {
        $count++;
        console.log(get(count));
    };
</script>

<h1>count: {$count}</h1>
<button on:click={onAdd}>+1</button>

自定义 store

只要一个对象实现了subscribe方法,那么就是一个store。我们可以通过svelte/store提供的模块来对状态进行封装。

在线 REPL

countStore.js

import { writable } from 'svelte/store';
export const useCount = () => {
    const { subscribe, set, update } = writable(0);

    return {
        subscribe,
        sub: () => update((val) => val - 1),
        add: () => update((val) => val + 1),
        reset: () => set(0)
    };
};

App.svelte

<script>
    import { useCount } from './countStore';
    const count = useCount();
</script>

<h1>count:{$count}</h1>
<button on:click={count.sub}>-</button>
<button on:click={count.add}>+</button>
<button on:click={count.reset}>reset</button>