mirror of https://github.com/sveltejs/svelte
parent
fd92394f57
commit
4b146b6661
@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
title: "`hydratable`"
|
||||||
|
---
|
||||||
|
|
||||||
|
In Svelte, when you want to render asynchonous content data on the server, you can simply `await` it. This is great! However, it comes with a major pitall: when hydrating that content on the client, Svelte has to redo the asynchronous work, which blocks hydration for however long it takes:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { getUser } from 'my-database-library';
|
||||||
|
|
||||||
|
// This will get the user on the server, render the user's name into the h1,
|
||||||
|
// and then, during hydration on the client, it will get the user _again_,
|
||||||
|
// blocking hydration until it's done.
|
||||||
|
const user = await getUser();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>{user.name}</h1>
|
||||||
|
```
|
||||||
|
|
||||||
|
That's silly, though. If we've already done the hard work of getting the data on the server, we don't want to get it again during hydration on the client. `hydratable` is a low-level API build to solve this problem. You probably won't need this very often -- it will probably be used behind the scenes by whatever datafetching library you use. For example, it powers [remote functions in SvelteKit](/docs/kit/remote-functions).
|
||||||
|
|
||||||
|
To fix the example above:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { hydratable } from 'svelte';
|
||||||
|
import { getUser } from 'my-database-library';
|
||||||
|
|
||||||
|
// During server rendering, this will serialize and stash the result of `getUser`, associating
|
||||||
|
// it with the provided key and baking it into the `head` content. During hydration, it will
|
||||||
|
// look for the serialized version, returning it instead of running `getUser`. After hydration
|
||||||
|
// is done, if it's called again, it'll simply invoke `getUser`.
|
||||||
|
const user = await hydratable('user', getUser());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>{user.name}</h1>
|
||||||
|
```
|
||||||
|
|
||||||
|
This API can also be used to provide access to random or time-based values that are stable between server rendering and hydration. For example, to get a random number that doesn't update on hydration:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { hydratable } from 'svelte';
|
||||||
|
const rand = hydratable('random', () => Math.random());
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're a library author, be sure to prefix the keys of your `hydratable` values with the name of your library so that your keys don't conflict with other libraries.
|
||||||
|
|
||||||
|
## Imperative API
|
||||||
|
|
||||||
|
If you're writing a library with separate server and client exports, it may be more convenient to use the imperative API:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { hydratable } from 'svelte';
|
||||||
|
|
||||||
|
const value = hydratable.get('foo'); // only works on the client
|
||||||
|
const hasValue = hydratable.has('foo');
|
||||||
|
hydratable.set('foo', 'whatever value you want'); // only works on the server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom serialization
|
||||||
|
|
||||||
|
By default, Svelte uses [`devalue`](https://npmjs.com/package/devalue) to serialize your data on the server so that decoding it on the client requires no dependencies. If you need to serialize additional things not covered by `devalue`, you can provide your own transport mechanisms by writing custom `encode` and `decode` methods.
|
||||||
|
|
||||||
|
### `encode`
|
||||||
|
|
||||||
|
Encode receives a value and outputs _the JavaScript code necessary to create that value on the client_. For example, Svelte's built-in encoder looks like this:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const encode = (value) => devalue.uneval(value);
|
||||||
|
encode(['hello', 'world']); // outputs `['hello', 'world']`
|
||||||
|
```
|
||||||
|
|
||||||
|
### `decode`
|
||||||
|
|
||||||
|
`decode` accepts whatever the JavaScript that `encode` outputs resolves to, and returns whatever the final value from `hydratable` should be.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
When using the isomorphic API, you must provide either `encode` or `decode`, depending on the environment. This enables your bundler to treeshake the unneeded code during your build:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { hydratable } from 'svelte';
|
||||||
|
import { BROWSER } from 'esm-env';
|
||||||
|
import { encode, decode } from '$lib/encoders';
|
||||||
|
|
||||||
|
const random = hydratable('random', () => Math.random(), { transport: BROWSER ? { decode } : { encode }});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
For the imperative API, you just provide `encode` or `decode` depending on which method you're using:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { hydratable } from 'svelte';
|
||||||
|
import { encode, decode } from '$lib/encoders';
|
||||||
|
|
||||||
|
const random = hydratable.get('random', { decode });
|
||||||
|
hydratable.set('random', Math.random(), { encode });
|
||||||
|
```
|
||||||
Loading…
Reference in new issue