mirror of https://github.com/sveltejs/svelte
parent
4d653f2635
commit
3e8ea124ea
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
|
||||
let count_value;
|
||||
|
||||
const unsubscribe = count.subscribe(value => {
|
||||
count_value = value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1>The count is {count_value}</h1>
|
||||
|
||||
<Incrementer/>
|
||||
<Decrementer/>
|
||||
<Resetter/>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function decrement() {
|
||||
// TODO decrement the count
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={decrement}>
|
||||
-
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function increment() {
|
||||
// TODO increment the count
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment}>
|
||||
+
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function reset() {
|
||||
// TODO reset the count
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={reset}>
|
||||
reset
|
||||
</button>
|
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const count = writable(0);
|
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
|
||||
let count_value;
|
||||
|
||||
const unsubscribe = count.subscribe(value => {
|
||||
count_value = value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1>The count is {count_value}</h1>
|
||||
|
||||
<Incrementer/>
|
||||
<Decrementer/>
|
||||
<Resetter/>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function decrement() {
|
||||
count.update(n => n - 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={decrement}>
|
||||
-
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function increment() {
|
||||
count.update(n => n + 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment}>
|
||||
+
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function reset() {
|
||||
count.set(0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={reset}>
|
||||
reset
|
||||
</button>
|
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const count = writable(0);
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Writable stores
|
||||
---
|
||||
|
||||
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. In `App.svelte`, `count` is a store, and we're setting `count_value` in the `count.subscribe` callback.
|
||||
|
||||
Click the `stores.js` tab to see the definition of `count`. It's a *writable* store, which means it has `set` and `update` methods in addition to `subscribe`.
|
||||
|
||||
Now go to the `Incrementer.svelte` tab so that we can wire up the `+` button:
|
||||
|
||||
```js
|
||||
function increment() {
|
||||
count.update(n => n + 1);
|
||||
}
|
||||
```
|
||||
|
||||
Clicking the `+` button should now update the count. Do the inverse for `Decrementer.svelte`.
|
||||
|
||||
Finally, in `Resetter.svelte`, implement `reset`:
|
||||
|
||||
```js
|
||||
function reset() {
|
||||
count.set(0);
|
||||
}
|
||||
```
|
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
|
||||
let count_value;
|
||||
|
||||
const unsubscribe = count.subscribe(value => {
|
||||
count_value = value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1>The count is {count_value}</h1>
|
||||
|
||||
<Incrementer/>
|
||||
<Decrementer/>
|
||||
<Resetter/>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function decrement() {
|
||||
count.update(n => n - 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={decrement}>
|
||||
-
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function increment() {
|
||||
count.update(n => n + 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment}>
|
||||
+
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function reset() {
|
||||
count.set(0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={reset}>
|
||||
reset
|
||||
</button>
|
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const count = writable(0);
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
</script>
|
||||
|
||||
<h1>The count is {$count}</h1>
|
||||
|
||||
<Incrementer/>
|
||||
<Decrementer/>
|
||||
<Resetter/>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function decrement() {
|
||||
count.update(n => n - 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={decrement}>
|
||||
-
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function increment() {
|
||||
count.update(n => n + 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={increment}>
|
||||
+
|
||||
</button>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
|
||||
function reset() {
|
||||
count.set(0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={reset}>
|
||||
reset
|
||||
</button>
|
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const count = writable(0);
|
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Auto-subscriptions
|
||||
---
|
||||
|
||||
The app in the previous example works, but there's a subtle bug — the `unsubscribe` function never gets called. If the component was instantiated and destroyed many times, this would result in a *memory leak*.
|
||||
|
||||
One way to fix it would be to use the `onDestroy` [lifecycle hook](tutorial/ondestroy):
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { onDestroy } from 'svelte';
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
|
||||
let count_value;
|
||||
|
||||
const unsubscribe = count.subscribe(value => {
|
||||
count_value = value;
|
||||
});
|
||||
|
||||
onDestroy(unsubscribe);
|
||||
</script>
|
||||
|
||||
<h1>The count is {count_value}</h1>
|
||||
```
|
||||
|
||||
It starts to get a bit boilerplatey though, especially if your component subscribes to multiple stores. Instead, Svelte has a trick up its sleeve — you can reference a store value by prefixing the store name with `$`:
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { count } from './stores.js';
|
||||
import Incrementer from './Incrementer.svelte';
|
||||
import Decrementer from './Decrementer.svelte';
|
||||
import Resetter from './Resetter.svelte';
|
||||
</script>
|
||||
|
||||
<h1>The count is {$count}</h1>
|
||||
```
|
||||
|
||||
You're not limited to using `$count` 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 — don't declare your own variables with a `$` prefix.
|
@ -0,0 +1,12 @@
|
||||
<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>
|
@ -0,0 +1,9 @@
|
||||
import { readable } from 'svelte/store';
|
||||
|
||||
export const time = readable(function start(set) {
|
||||
// implementation goes here
|
||||
|
||||
return function stop() {
|
||||
|
||||
};
|
||||
});
|
@ -0,0 +1,12 @@
|
||||
<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>
|
@ -0,0 +1,11 @@
|
||||
import { readable } from 'svelte/store';
|
||||
|
||||
export const time = readable(function start(set) {
|
||||
const interval = setInterval(() => {
|
||||
set(new Date());
|
||||
}, 1000);
|
||||
|
||||
return function stop() {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, new Date());
|
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: 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.
|
||||
|
||||
Click over to the `stores.js` tab. The first argument to `readable` 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. The second (optional) argument is the initial value.
|
||||
|
||||
```js
|
||||
export const time = readable(function start(set) {
|
||||
const interval = setInterval(() => {
|
||||
set(new Date());
|
||||
}, 1000);
|
||||
|
||||
return function stop() {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, new Date());
|
||||
```
|
@ -0,0 +1,17 @@
|
||||
<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>
|
@ -0,0 +1,18 @@
|
||||
import { readable, derive } from 'svelte/store';
|
||||
|
||||
export const time = readable(function start(set) {
|
||||
const interval = setInterval(() => {
|
||||
set(new Date());
|
||||
}, 1000);
|
||||
|
||||
return function stop() {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, new Date());
|
||||
|
||||
const start = new Date();
|
||||
|
||||
export const elapsed = derive(
|
||||
time,
|
||||
$time => {}
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
<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>
|
@ -0,0 +1,18 @@
|
||||
import { readable, derive } from 'svelte/store';
|
||||
|
||||
export const time = readable(function start(set) {
|
||||
const interval = setInterval(() => {
|
||||
set(new Date());
|
||||
}, 1000);
|
||||
|
||||
return function stop() {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, new Date());
|
||||
|
||||
const start = new Date();
|
||||
|
||||
export const elapsed = derive(
|
||||
time,
|
||||
$time => Math.round(($time - start) / 1000)
|
||||
);
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Derived stores
|
||||
---
|
||||
|
||||
You can create a store whose value is based on the value of one or more *other* stores with `derive`. Building on our previous example, we can create a store that derives the time the page has been open:
|
||||
|
||||
```js
|
||||
export const elapsed = derive(
|
||||
time,
|
||||
$time => Math.round(($time - start) / 1000)
|
||||
);
|
||||
```
|
||||
|
||||
> It's possible to derive a store from multiple inputs, and to explicitly `set` a value instead of returning it (which is useful for deriving values asynchronously). Consult the [API reference](docs/TK) for more information.
|
@ -0,0 +1,9 @@
|
||||
<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>
|
@ -0,0 +1,14 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
function createCount() {
|
||||
const { subscribe, set, update } = writable(0);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
increment: () => {},
|
||||
decrement: () => {},
|
||||
reset: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
export const count = createCount();
|
@ -0,0 +1,9 @@
|
||||
<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>
|
@ -0,0 +1,14 @@
|
||||
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();
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: 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.
|
||||
|
||||
For example, the `count` store from our earlier example could include `increment`, `decrement` and `reset` methods and avoid exposing `set` and `update`:
|
||||
|
||||
```js
|
||||
function createCount() {
|
||||
const { subscribe, set, update } = writable(0);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
increment: () => update(n => n + 1),
|
||||
decrement: () => update(n => n - 1),
|
||||
reset: () => set(0)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Stores"
|
||||
}
|
Loading…
Reference in new issue