注册 登录

清河洛

svelte中的状态管理

qingheluo2023-09-03清河洛813
props可以由父组件传递给子组件,但是一个应用程序是由很多组件通过复杂的组合而成的,并不单单仅存在父子关系的组件两个非父子组件之间想要传递数据时,就需要用到svelte内置的状态管理store,类似于vue中的Vuex当期望脱离组件的层级(父-子)关系且能够在任意位置都能访问某个状态(变量)时,状态管理是非常有用的状态分为可写状态(writable)和只读状态(readable)两种类型,需要从"svelte/store"中引入已经为我们创建好的工厂函数一般我们会将创建的状态数据单独放置于一个js文件中,以便所有需要此数据的组件引用可写状态(writable)创建可写状态:writab...

props可以由父组件传递给子组件,但是一个应用程序是由很多组件通过复杂的组合而成的,并不单单仅存在父子关系的组件

两个非父子组件之间想要传递数据时,就需要用到svelte内置的状态管理store,类似于vue中的Vuex

当期望脱离组件的层级(父-子)关系且能够在任意位置都能访问某个状态(变量)时,状态管理是非常有用的

状态分为可写状态(writable)和只读状态(readable)两种类型,需要从"svelte/store"中引入已经为我们创建好的工厂函数

一般我们会将创建的状态数据单独放置于一个js文件中,以便所有需要此数据的组件引用

可写状态(writable)

创建可写状态:writable(val,func)

val为该状态的初始值
func为一个函数,一般我们定义为start,在该状态被首次订阅时会运行,后续订阅不会再次运行
    start(set){return function stop(){}}
    set参数用于设置该状态的值:set(val)
    返回一个函数,我们一般定义为stop,该函数会在所有订阅者取消订阅时运行
    func参数可以省略,通过状态的set()方法设置值

let n=0;
export let num = writable(n, function start(set){
    // 若干首次订阅时的运行代码
    let interval = setInterval(() => set(n++), 1000);
    return function stop(){ clearInterval(interval); }
});

1、当第一次订阅时运行start函数,并返回初始值0
2、每秒运行set()一次,将该状态的值加1
3、返回stop函数用于所有订阅社取消订阅后取消任务,避免内存泄漏

以上仅为一个示例,实际使用中可写状态一般不会设置为动态
而是由各个引用的组件通过set()方法设置其值
以便在所有引用的组件中共享数据

一个可写的状态有三个方法

update(func):更新状态
    func函数接受一个参数:状态当前的值
set(val):重置状态
    不管该状态当前的值,直接重新设置当前状态的值为val
subscribe(func):添加订阅
    当状态发生改变时,会通知所有订阅者,此时订阅者会自动运行func
    func接受一个参数:状态变化后的值
    该方法返回一个函数,调用后会取消订阅
    如果监听该状态的组件被实例化后不取消监听就直接销毁,将导致内存泄露
    一般会在组件生命周期函数onDestroy()中运行
        onDestroy(unsubscribe);
编辑用于存放状态创建的js文件,如store.js

import { writable } from "svelte/store";
export let num = writable(0);

我们已经创建好状态并使用export语句导出,在需要此状态的组件中引入即可

创建add.svelte组件和reset.svelte组件

add.svelte
<script>
    import { num } from "./store.js";
    function add() {
        num.update((n) => n + 1);
    }
</script>
<button on:click={add}>+1</button>
<!--
<button on:click={num.update((n) => n + 1)}>+1</button>
如果直接将逻辑写在行内,实测不生效
不知道是bug还是设定
-->

reset.svelte
<script>
    import { num } from "./store.js";
    function reset() {
        num.reset(0);
    }
</script>
<button on:click={reset}>reset</button>

在总文件中引入状态和两个模板

<script>
    import { num } from "path/store.js";
    import Add from "path/add.svelte";
    import Reset from "path/reset.svelte";
    import {onDestroy} from "svelte";
    let val;
    let unsubscribe = num.subscribe((n) => {
        val = n;
    });
    onDestroy(unsubscribe);
</script>

<p>num的值为:{val}</p>
<Add />
<Reset />

我们可以看出,要获取一个状态的值,需要:

1、设置一个接受的变量
2、然后设置状态监听
3、组件销毁时的取消监听
当使用多个状态时将变得繁琐且易出错

svelte为我们设置了一个简化的方式,就是在获取状态的值时在名称前面添加一个 $ 前缀,就会自动为我们设置好上面的步骤,称之为自动订阅

甚至可以在js代码中直接使用

<script>
    import { num } from "path/store.js";
    import Add from "path/add.svelte";
    import Reset from "path/reset.svelte";

    $: double = $num * 2;
</script>

<p>num的值为:{$num}</p>
<p>num的2倍为:{$double}</p>
<Add />
<Reset />

自动订阅仅适用于在组件的顶层范围声明(或者导入的JS文件中)的stroe变量

svelte会认为所有以 $ 开头的变量名都表示引用某个store值,svelte会禁止使用 $ 开头的自定义变量名

只读状态(readable)

创建只读状态:readable(val,func)

func参数参考可写状态的创建

可写状态仅有一个subscribe(func)方法用于添加订阅
不过实际中我们一般使用自动订阅,也就是 $ 前缀来获取值

衍生状态(或称为继承状态)

所谓状态的衍生,就是止一个状态的值时基于另一个状态的值来设置,另一个状态的值发生改变该状态也会改变

使用derived(val,func)方法创建一个衍生状态

val表示要衍生的状态名
func是一个函数,用于返回该状态的值
import { writable, derived } from 'svelte/store';

let num = writable(0);
let double = derived(
    num,
    function ($num) {
        return $num * 2;
    }
)

export { num , bouble }

可以在func中直接使用 $ 前缀获取状态的值

只要一个对象正确地实现了 subscribe 方法,它即是一个 store



网址导航