Getting started with Svelte - St

2023-11-29  本文已影响0人  游文影月志

Not all application state belongs inside your application's component hierarchy. Sometimes, you'll have values that need to be accessed by multiple unrelated components, or by a regular JavaScript module.

In Svelte, we do this with stores. A store is simply an object with a subscribe method that allows interested parties to be notified whenever the store value changes.

Writable stores

Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional set and update methods.

set is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it.

update is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store.

import { writable } from 'svelte/store';


export const count = writable(0);


count.subscribe((value) => {
    console.log(value);
}); // logs '0'


count.set(1); // logs '1'


count.update((n) => n + 1); // logs '2'

Auto subscriptions

If the store is subscribed to, but never unsubscribed and the component was instantiated and destroyed many times, this would result in a memory leak.

You can reference a store value by prefixing the store name with $, this will auto subscribe and unsubscribe the store.

You're not limited to using $ started store inside the markup, either — you can use it anywhere in the <script> as well, such as in event handlers or reactive declarations.

Any name beginning with $ is assumed to refer to a store value. It's effectively a reserved character — Svelte will prevent you from declaring your own variables with a $ prefix.

Note that auto subscription only works with store variables that are declared (or imported) at the top-level scope of a component.

Readable stores

Not all stores should be writable by whoever has a reference to them. For example, you might have a store representing the mouse position or the user's geolocation, and it doesn't make sense to be able to set those values from 'outside'. For those cases, we have readable stores.

The first argument to readable is an initial value, which can be null or undefined if you don't have one yet. The second argument is a start function that takes a set callback and returns a stop function. The start function is called when the store gets its first subscriber; stop is called when the last subscriber unsubscribes.

stores.js

import { readable } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
    const interval = setInterval(() => {
        set(new Date());
    }, 1000);

    return function stop() {
        clearInterval(interval);
    };
});

App.svelte

<script>
    import { time } from './stores.js';

    const formatter = new Intl.DateTimeFormat(
        'en',
        {
            hour12: true,
            hour: 'numeric',
            minute: '2-digit',
            second: '2-digit'
        }
    );
</script>

<h1>The time is {formatter.format($time)}</h1>

Derived stores

You can create a store whose value is based on the value of one or more other stores with derived.

It's possible to derive a store from multiple input stores, and to explicitly set a value instead of returning it (which is useful for deriving values asynchronously).

stores.js

import { readable, derived } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
    const interval = setInterval(() => {
        set(new Date());
    }, 1000);

    return function stop() {
        clearInterval(interval);
    };
});

const start = new Date();

export const elapsed = derived(
    time,
    ($time) => Math.round(($time - start) / 1000)
);

App.svelte

<script>
    import { time, elapsed } from './stores.js';

    const formatter = new Intl.DateTimeFormat(
        'en',
        {
            hour12: true,
            hour: 'numeric',
            minute: '2-digit',
            second: '2-digit'
        }
    );
</script>

<h1>The time is {formatter.format($time)}</h1>

<p>
    This page has been open for
    {$elapsed}
    {$elapsed === 1 ? 'second' : 'seconds'}
</p>

Custom stores

As long as an object correctly implements the subscribe method, it's a store. Beyond that, anything goes. It's very easy, therefore, to create custom stores with domain-specific logic.

stores.js

import { writable } from 'svelte/store';

function createCount() {
    const { subscribe, set, update } = writable(0);

    return {
        subscribe,
        increment: () => update((n) => n + 1),
        decrement: () => update((n) => n - 1),
        reset: () => set(0)
    };
}

export const count = createCount();

App.svelte

<script>
    import { count } from './stores.js';
</script>

<h1>The count is {$count}</h1>

<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>reset</button>

Store bindings

If a store is writable — i.e. it has a set method — you can bind to its value, just as you can bind to local component state.

stores.js

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

export const name = writable('world');

export const greeting = derived(name, ($name) => `Hello ${$name}!`);

App.svelte

<script>
    import { name, greeting } from './stores.js';
</script>

<h1>{$greeting}</h1>
<input bind:value={$name} />

<button on:click={() => $name += '!'}>
    Add exclamation mark!
</button>

Changing the input value will now update name and all its dependents.

The $name += '!' assignment is equivalent to name.set($name + '!').

上一篇 下一篇

猜你喜欢

热点阅读