update docs

This commit is contained in:
Rich Harris
2019-04-30 12:10:53 -04:00
parent bca88831da
commit dc73973d44
13 changed files with 175 additions and 229 deletions

View File

@@ -12,12 +12,12 @@ title: Introduction
Sapper is a framework for building extremely high-performance web apps. You're looking at one right now! There are two basic concepts:
* Each page of your app is a [Svelte](https://svelte.technology) component
* Each page of your app is a [Svelte](https://svelte.dev) component
* You create pages by adding files to the `src/routes` directory of your project. These will be server-rendered so that a user's first visit to your app is as fast as possible, then a client-side app takes over
Building an app with all the modern best practices — code-splitting, offline support, server-rendered views with client-side hydration — is fiendishly complicated. Sapper does all the boring stuff for you so that you can get on with the creative part.
You don't need to know Svelte to understand the rest of this guide, but it will help. In short, it's a UI framework that compiles your components to highly optimized vanilla JavaScript. Read the [introductory blog post](https://svelte.technology/blog/frameworks-without-the-framework) and the [guide](https://svelte.technology/guide) to learn more.
You don't need to know Svelte to understand the rest of this guide, but it will help. In short, it's a UI framework that compiles your components to highly optimized vanilla JavaScript. Read the [introductory blog post](https://svelte.dev/blog/svelte-3-rethinking-reactivity) and the [tutorial](https://svelte.dev/tutorial) to learn more.
### Why the name?
@@ -32,7 +32,7 @@ For web developers, the stakes are generally lower than for combat engineers. Bu
[Next.js](https://github.com/zeit/next.js) is a React framework from [Zeit](https://zeit.co), and is the inspiration for Sapper. There are a few notable differences, however:
* Sapper is powered by Svelte instead of React, so it's faster and your apps are smaller
* Instead of route masking, we encode route parameters in filenames (see the [routing](guide#routing) section below)
* Instead of route masking, we encode route parameters in filenames (see the [routing](docs#routing) section below)
* As well as *pages*, you can create *server routes* in your `src/routes` directory. This makes it very easy to, for example, add a JSON API such as the one powering this very page (try visiting [/docs.json](/docs.json))
* Links are just `<a>` elements, rather than framework-specific `<Link>` components. That means, for example, that [this link right here](/), despite being inside a blob of markdown, works with the router as you'd expect

View File

@@ -11,8 +11,8 @@ If you take a look inside the [sapper-template](https://github.com/sveltejs/sapp
├ src
│ ├ routes
│ │ ├ # your routes here
│ │ ├ _error.html
│ │ └ index.html
│ │ ├ _error.svelte
│ │ └ index.svelte
│ ├ client.js
│ ├ server.js
│ ├ service-worker.js
@@ -24,9 +24,9 @@ If you take a look inside the [sapper-template](https://github.com/sveltejs/sapp
When you first run Sapper, it will create an additional `__sapper__` directory containing generated files.
You'll notice a few extra files and a `cypress` directory which relates to [testing](guide#testing) — we don't need to worry about those right now.
You'll notice a few extra files and a `cypress` directory which relates to [testing](docs#testing) — we don't need to worry about those right now.
> You *can* create these files from scratch, but it's much better to use the template. See [getting started](guide#getting-started) for instructions on how to easily clone it
> You *can* create these files from scratch, but it's much better to use the template. See [getting started](docs#getting-started) for instructions on how to easily clone it
### package.json
@@ -35,9 +35,9 @@ Your package.json contains your app's dependencies and defines a number of scrip
* `npm run dev` — start the app in development mode, and watch source files for changes
* `npm run build` — build the app in production mode
* `npm run export` — bake out a static version, if applicable (see [exporting](guide#exporting))
* `npm run export` — bake out a static version, if applicable (see [exporting](docs#exporting))
* `npm start` — start the app in production mode after you've built it
* `npm test` — run the tests (see [testing](guide#testing))
* `npm test` — run the tests (see [testing](docs#testing))
### src
@@ -46,17 +46,17 @@ This contains the three *entry points* for your app — `src/client.js`, `src/se
#### src/client.js
This *must* import, and call, the `start` function from the generated `__sapper__/client.js` file:
This *must* import, and call, the `start` function from the generated `@sapper/app` module:
```js
import * as sapper from '../__sapper__/client.js';
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
});
```
In many cases, that's the entirety of your entry module, though you can do as much or as little here as you wish. See the [client API](guide#client-api) section for more information on functions you can import.
In many cases, that's the entirety of your entry module, though you can do as much or as little here as you wish. See the [client API](docs#client-api) section for more information on functions you can import.
#### src/server.js
@@ -64,7 +64,7 @@ In many cases, that's the entirety of your entry module, though you can do as mu
This is a normal Express (or [Polka](https://github.com/lukeed/polka), etc) app, with three requirements:
* it should serve the contents of the `static` folder, using for example [sirv](https://github.com/lukeed/sirv)
* it should call `app.use(sapper.middleware())` at the end, where `sapper` is imported from `../__sapper__/server.js`
* it should call `app.use(sapper.middleware())` at the end, where `sapper` is imported from `@sapper/server`
* it must listen on `process.env.PORT`
Beyond that, you can write the server however you like.
@@ -76,7 +76,7 @@ Service workers act as proxy servers that give you fine-grained control over how
Among other things, this makes it possible to build applications that work offline.
Because every app needs a slightly different service worker (sometimes it's appropriate to always serve from the cache, sometimes that should only be a last resort in case of no connectivity), Sapper doesn't attempt to control the service worker. Instead, you write the logic in `service-worker.js`. You can import any of the following from `../__sapper__/service-worker.js`:
Because every app needs a slightly different service worker (sometimes it's appropriate to always serve from the cache, sometimes that should only be a last resort in case of no connectivity), Sapper doesn't attempt to control the service worker. Instead, you write the logic in `service-worker.js`. You can import any of the following from `@sapper/service-worker`:
* `files` — an array of files found in the `static` directory
* `shell` — the client-side JavaScript generated by the bundler (Rollup or webpack)
@@ -88,7 +88,7 @@ Because every app needs a slightly different service worker (sometimes it's appr
This file is a template for responses from the server. Sapper will inject content that replaces the following tags:
* `%sapper.base%` — a `<base>` element (see [base URLs](guide#base-urls))
* `%sapper.base%` — a `<base>` element (see [base URLs](docs#base-urls))
* `%sapper.styles%` — critical CSS for the page being requested
* `%sapper.head%` — HTML representing page-specific `<head>` contents, like `<title>`
* `%sapper.html%` — HTML representing the body of the page being rendered
@@ -97,14 +97,14 @@ This file is a template for responses from the server. Sapper will inject conten
### src/routes
This is the meat of your app — the pages and server routes. See the section on [routing](guide#routing) for the juicy details.
This is the meat of your app — the pages and server routes. See the section on [routing](docs#routing) for the juicy details.
### static
This is a place to put any files that your app uses — fonts, images and so on. For example `static/favicon.png` will be served as `/favicon.png`.
Sapper doesn't serve these files — you'd typically use [sirv](https://github.com/lukeed/sirv) or [serve-static](https://github.com/expressjs/serve-static) for that — but it will read the contents of the `static` folder so that you can easily generate a cache manifest for offline support (see [service-worker.js](guide#templates-service-worker-js)).
Sapper doesn't serve these files — you'd typically use [sirv](https://github.com/lukeed/sirv) or [serve-static](https://github.com/expressjs/serve-static) for that — but it will read the contents of the `static` folder so that you can easily generate a cache manifest for offline support (see [service-worker.js](docs#templates-service-worker-js)).
### rollup.config.js / webpack.config.js

View File

@@ -7,12 +7,12 @@ As we've seen, there are two types of route in Sapper — pages, and server rout
### Pages
Pages are Svelte components written in `.html` files. When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel.
Pages are Svelte components written in `.svelte` files. When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel.
The filename determines the route. For example, `src/routes/index.html` is the root of your site:
The filename determines the route. For example, `src/routes/index.svelte` is the root of your site:
```html
<!-- src/routes/index.html -->
<!-- src/routes/index.svelte -->
<svelte:head>
<title>Welcome</title>
</svelte:head>
@@ -20,10 +20,10 @@ The filename determines the route. For example, `src/routes/index.html` is the r
<h1>Hello and welcome to my site!</h1>
```
A file called either `src/routes/about.html` or `src/routes/about/index.html` would correspond to the `/about` route:
A file called either `src/routes/about.svelte` or `src/routes/about/index.svelte` would correspond to the `/about` route:
```html
<!-- src/routes/about.html -->
<!-- src/routes/about.svelte -->
<svelte:head>
<title>About</title>
</svelte:head>
@@ -35,7 +35,30 @@ A file called either `src/routes/about.html` or `src/routes/about/index.html` wo
Dynamic parameters are encoded using `[brackets]`. For example, here's how you could create a page that renders a blog post:
```html
<!-- src/routes/blog/[slug].html -->
<!-- src/routes/blog/[slug].svelte -->
<script context="module">
// the (optional) preload function takes a
// `{ path, params, query }` object and turns it into
// the data we need to render the page
export async function preload(page, session) {
// the `slug` parameter is available because this file
// is called [slug].svelte
const { slug } = page.params;
// `this.fetch` is a wrapper around `fetch` that allows
// you to make credentialled requests on both
// server and client
const res = await this.fetch(`blog/${slug}.json`);
const article = await res.json();
return { article };
}
</script>
<script>
export let article;
</script>
<svelte:head>
<title>{article.title}</title>
</svelte:head>
@@ -43,28 +66,11 @@ Dynamic parameters are encoded using `[brackets]`. For example, here's how you c
<h1>{article.title}</h1>
<div class='content'>
{@html article.html}
{@html article.svelte}
</div>
<script>
export default {
// the (optional) preload function takes a
// `{ params, query }` object and turns it into
// the data we need to render the page
preload({ params, query }) {
// the `slug` parameter is available because this file
// is called [slug].html
const { slug } = params;
return this.fetch(`blog/${slug}.json`).then(r => r.json()).then(article => {
return { article };
});
}
};
</script>
```
> See the section on [preloading](guide#preloading) for more info about `preload` and `this.fetch`
> See the section on [preloading](docs#preloading) for more info about `preload` and `this.fetch`
### Server routes
@@ -98,15 +104,15 @@ export async function get(req, res, next) {
There are three simple rules for naming the files that define your routes:
* A file called `src/routes/about.html` corresponds to the `/about` route. A file called `src/routes/blog/[slug].html` corresponds to the `/blog/:slug` route, in which case `params.slug` is available to `preload`
* The file `src/routes/index.html` corresponds to the root of your app. `src/routes/about/index.html` is treated the same as `src/routes/about.html`.
* A file called `src/routes/about.svelte` corresponds to the `/about` route. A file called `src/routes/blog/[slug].svelte` corresponds to the `/blog/:slug` route, in which case `params.slug` is available to `preload`
* The file `src/routes/index.svelte` corresponds to the root of your app. `src/routes/about/index.svelte` is treated the same as `src/routes/about.svelte`.
* Files and directories with a leading underscore do *not* create routes. This allows you to colocate helper modules and components with the routes that depend on them — for example you could have a file called `src/routes/_helpers/datetime.js` and it would *not* create a `/_helpers/datetime` route
### Error page
In addition to regular pages, there is a 'special' page that Sapper expects to find — `src/routes/_error.html`. This will be shown when an error occurs while rendering a page.
In addition to regular pages, there is a 'special' page that Sapper expects to find — `src/routes/_error.svelte`. This will be shown when an error occurs while rendering a page.
The `error` object is made available to the template along with the HTTP `status` code.
@@ -116,6 +122,6 @@ The `error` object is made available to the template along with the HTTP `status
You can use a subset of regular expressions to qualify route parameters, by placing them in parentheses after the parameter name.
For example, `src/routes/items/[id([0-9]+)].html` would only match numeric IDs — `/items/123` would match, but `/items/xyz` would not.
For example, `src/routes/items/[id([0-9]+)].svelte` would only match numeric IDs — `/items/123` would match, but `/items/xyz` would not.
Because of technical limitations, the following characters cannot be used: `/`, `\`, `?`, `:`, `(` and `)`.

View File

@@ -2,20 +2,19 @@
title: Client API
---
The `__sapper__/client.js` module contains functions for controlling your app and responding to events.
The `@sapper/app` module, which is generated by Sapper based on the shape of your app, contains functions for controlling Sapper programmatically and responding to events.
### start({ target, store? })
### start({ target })
* `target` — an element to render pages to
* `store` — an function that, given some data, returns a Store object. See the [state management](https://sapper.svelte.technology/guide#state-management) section for more detail
This configures the router and starts the application — listens for clicks on `<a>` elements, interacts with the `history` API, and renders and updates your Svelte components.
Returns a `Promise` that resolves when the initial page has been hydrated.
```js
import * as sapper from '../__sapper__/client.js';
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
@@ -37,7 +36,7 @@ Programmatically navigates to the given `href`. If the destination is a Sapper r
* `href` — the page to prefetch
Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `preload` method with the appropriate options. This is the same behaviour that Sapper triggers when the user taps or mouses over an `<a>` element with [rel=prefetch](guide#prefetching).
Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `preload` method with the appropriate options. This is the same behaviour that Sapper triggers when the user taps or mouses over an `<a>` element with [rel=prefetch](docs#prefetching).
@@ -45,4 +44,4 @@ Programmatically prefetches the given page, which means a) ensuring that the cod
* `routes` — an optional array of strings representing routes to prefetch
Programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this after `sapper.start()` is complete, to speed up subsequent navigation (this is the 'L' of the [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/)). Omitting arguments will cause all routes to be fetched, or you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.html`) or `/blog/*` (to match `src/routes/blog/[slug].html`). Unlike `prefetch`, this won't call `preload` for individual pages.
Programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this after `sapper.start()` is complete, to speed up subsequent navigation (this is the 'L' of the [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/)). Omitting arguments will cause all routes to be fetched, or you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.svelte`) or `/blog/*` (to match `src/routes/blog/[slug].svelte`). Unlike `prefetch`, this won't call `preload` for individual pages.

View File

@@ -2,46 +2,37 @@
title: Preloading
---
As seen in the [routing](guide#routing) section, top-level page components can have a `preload` function that will load some data that the page depends on. This is similar to `getInitialProps` in Next.js or `asyncData` in Nuxt.js.
As seen in the [routing](docs#routing) section, page components can have an optional `preload` function that will load some data that the page depends on. This is similar to `getInitialProps` in Next.js or `asyncData` in Nuxt.js.
```html
<script>
export default {
preload({ params, query }) {
const { slug } = params;
<script context="module">
export async function preload(page, session) {
const { slug } = page.params;
return this.fetch(`blog/${slug}.json`).then(r => r.json()).then(article => {
return { article };
});
}
};
const res = await this.fetch(`blog/${slug}.json`);
const article = await res.json();
return { article };
}
</script>
```
Your `preload` function is optional; whether or not you include it, the component will have access to the `query` and `params` objects, on top of any [default data](https://svelte.technology/guide#default-data) specified with a `data` property.
The top-level `_layout.html` component is rendered with a `preloading` value: `true` during preloading, `false` otherwise. This value is useful to display a loading spinner or otherwise indicate that a navigation is in progress.
```html
<!-- src/routes/_layout.html -->
{#if preloading}
<div>Loading...</div>
{/if}
<svelte:component this={child.component} {...child.props}/>
```
The `preloading` value is only set during page navigations. Prefetching (see [below](guide#prefetching)) does not set `preloading` since it is intended to be transparent to the user.
It lives in a `context="module"` script — see the [tutorial](https://svelte.dev/tutorial/module-exports) — because it's not part of the component instance itself; instead, it runs *before* the component is created, allowing you to avoid flashes while data is fetched.
### Argument
The `preload` function receives a `{ params, query }` object where `params` is derived from the URL and the route filename, and `query` is an object of values in the query string.
The `preload` function receives two arguments — `page` and `session`.
So if the example above was `src/routes/blog/[slug].html` and the URL was `/blog/some-post?foo=bar&baz`, the following would be true:
`page` is a `{ path, params, query }` object where `path` is the URL's pathname, `params` is derived from `path` and the route filename, and `query` is an object of values in the query string.
* `params.slug === 'some-post'`
* `query.foo === 'bar'`
* `query.baz === true`
So if the example above was `src/routes/blog/[slug].svelte` and the URL was `/blog/some-post?foo=bar&baz`, the following would be true:
* `page.path === '/blog/some-post'`
* `page.params.slug === 'some-post'`
* `page.query.foo === 'bar'`
* `page.query.baz === true`
`session` is generated on the server by the `session` option passed to `sapper.middleware` (TODO this needs further documentation. Perhaps a server API section?)
### Return value
@@ -52,14 +43,12 @@ When Sapper renders a page on the server, it will attempt to serialize the resol
### Context
Inside `preload`, you have access to three methods...
Inside `preload`, you have access to three methods:
* `this.fetch(url, options)`
* `this.error(statusCode, error)`
* `this.redirect(statusCode, location)`
...and `this.store`, if you're using [state management](guide#state-management).
#### this.fetch
@@ -68,16 +57,14 @@ In browsers, you can use `fetch` to make AJAX requests, for getting data from yo
To fix this, Sapper provides `this.fetch`, which works on the server as well as in the client:
```html
<script>
export default {
preload() {
return this.fetch(`secret-data.json`, {
credentials: 'include'
}).then(r => {
// ...
});
}
};
<script context="module">
export async function preload() {
const res = await this.fetch(`secret-data.json`, {
credentials: 'include'
});
// ...
}
</script>
```
@@ -89,23 +76,19 @@ Note that you will need to use session middleware such as [express-session](http
If the user navigated to `/blog/some-invalid-slug`, we would want to render a 404 Not Found page. We can do that with `this.error`:
```html
<script>
export default {
preload({ params, query }) {
const { slug } = params;
<script context="module">
export async function preload({ params, query }) {
const { slug } = params;
return this.fetch(`blog/${slug}.json`).then(r => {
// assume all responses are either 200 or 404
if (r.status === 200) {
return r.json().then(article => {
return { article };
});
} else {
this.error(404, 'Not found');
}
});
const res = await this.fetch(`blog/${slug}.json`);
if (res.status === 200) {
const article = await res.json();
return { article };
}
};
this.error(404, 'Not found');
}
</script>
```
@@ -117,19 +100,15 @@ The same applies to other error codes you might encounter.
You can abort rendering and redirect to a different location with `this.redirect`:
```html
<script>
export default {
preload({ params, session }) {
const { user } = this.store.get();
<script context="module">
export async function preload(page, session) {
const { user } = session;
if (!user) {
return this.redirect(302, 'login');
}
return {
user
};
if (!user) {
return this.redirect(302, 'login');
}
};
return { user };
}
</script>
```

View File

@@ -6,39 +6,39 @@ So far, we've treated pages as entirely standalone components — upon navigatio
But in many apps, there are elements that should be visible on *every* page, such as top-level navigation or a footer. Instead of repeating them in every page, we can use *layout* components.
To create a layout component that applies to every page, make a file called `src/routes/_layout.html`. The default layout component (the one that Sapper uses if you don't bring your own) looks like this...
To create a layout component that applies to every page, make a file called `src/routes/_layout.svelte`. The default layout component (the one that Sapper uses if you don't bring your own) looks like this...
```html
<svelte:component this={child.component} {...child.props}/>
<slot></slot>
```
...but we can add whatever markup, styles and behaviour we want. For example, let's add a nav bar:
```html
<!-- src/routes/_layout.html -->
<!-- src/routes/_layout.svelte -->
<nav>
<a href=".">Home</a>
<a href="about">About</a>
<a href="settings">Settings</a>
</nav>
<svelte:component this={child.component} {...child.props}/>
<slot></slot>
```
Sapper computes the `child` property based on which page the user has navigated to. If we create pages for `/`, `/about` and `/settings`...
If we create pages for `/`, `/about` and `/settings`...
```html
<!-- src/routes/index.html -->
<!-- src/routes/index.svelte -->
<h1>Home</h1>
```
```html
<!-- src/routes/about.html -->
<!-- src/routes/about.svelte -->
<h1>About</h1>
```
```html
<!-- src/routes/settings.html -->
<!-- src/routes/settings.svelte -->
<h1>Settings</h1>
```
@@ -52,7 +52,7 @@ Suppose we don't just have a single `/settings` page, but instead have nested pa
We can create a layout that only applies to pages below `/settings` (while inheriting the root layout with the top-level nav):
```html
<!-- src/routes/settings/_layout.html -->
<!-- src/routes/settings/_layout.svelte -->
<h1>Settings</h1>
<div class="submenu">
@@ -60,47 +60,27 @@ We can create a layout that only applies to pages below `/settings` (while inher
<a href="settings/notifications">Notifications</a>
</div>
<svelte:component this={child.component} {...child.props}/>
<slot></slot>
```
In addition to `child.component` and `child.props`, there is a `child.segment` property which is useful for things like styling:
Layout components receive a `segment` property which is useful for things like styling:
```diff
+<script>
+ export let segment;
+</script>
+
<div class="submenu">
- <a href="settings/profile">Profile</a>
- <a href="settings/notifications">Notifications</a>
+ <a
+ class={child.segment === "profile" ? "selected" : ""}
+ class:selected={segment === "profile"}
+ href="settings/profile"
+ >Profile</a>
+
+ <a
+ class={child.segment === "notifications" ? "selected" : ""}
+ class:selected={segment === "notifications"}
+ href="settings/notifications"
+ >Notifications</a>
</div>
```
### Preloading in layouts
Like page components, layout components can use `preload`:
```html
<!-- src/routes/foo/_layout.html -->
<svelte:component
this={child.component}
someData={thingAllChildComponentsWillNeed}
{...child.props}
/>
<script>
export default {
async preload() {
return {
thingAllChildComponentsWillNeed: await loadSomeData()
};
}
};
</script>
```

View File

@@ -2,7 +2,7 @@
title: Server-side rendering
---
Sapper, by default, renders server-side first (SSR), and then re-mounts any dynamic elements on the client. Svelte provides [excellent support for this](https://svelte.technology/guide#server-side-rendering). This has benefits in performance and search engine indexing, among others, but comes with its own set of complexities.
Sapper, by default, renders server-side first (SSR), and then re-mounts any dynamic elements on the client. Svelte provides [excellent support for this](https://svelte.dev/docs#server-side-rendering). This has benefits in performance and search engine indexing, among others, but comes with its own set of complexities.
### Making a component SSR compatible
@@ -14,24 +14,19 @@ Since there is no `window` in a server-side environment like Sapper's, the actio
ReferenceError: window is not defined
```
The way to get around this is to use a dynamic import for your component, from within the `oncreate` hook (which is only called on the client), so that your import code is never called on the server.
```js
export default {
async oncreate () {
const MyComponent = await import('my-non-ssr-component')
this.set({ MyComponent })
}
}
```
You can then use your component within your app:
The way to get around this is to use a dynamic import for your component, from within the `onMount` function (which is only called on the client), so that your import code is never called on the server.
```html
<svelte:component this={MyComponent}
{prop1}
prop2="foo"
/>
```
<script>
import { onMount } from 'svelte';
> It might be the case that your component uses a default export, which actually ends up being an object with a property called `default`, in which case you would import it as `{ default: MyComponent }` rather than just `MyComponent`
let MyComponent;
onMount(async () => {
const module = await import('my-non-ssr-component');
MyComponent = module.default;
});
</script>
<svelte:component this={MyComponent} foo="bar"/>
```

View File

@@ -1,55 +1,40 @@
---
title: State management
title: Stores
---
Sapper integrates with the built-in Svelte store. If you're not familiar with Store, read the [Svelte state management](https://svelte.technology/guide#state-management) guide before continuing with this section.
The `page` and `session` values passed to `preload` functions are available to components as [stores](https://svelte.dev/tutorial/writable-stores), along with `preloading`.
To use Store, you must integrate it with your server and client apps separately.
Inside a component, get references to the stores like so:
### On the server
```html
<script>
import { stores } from '@sapper/app';
const { preloading, page, session } = stores();
</script>
```
Whereas the client-side app has a single Store instance that lasts as long as the page is open, the server-side app must create a new store for each request:
* `preloading` contains a readonly boolean value, indicating whether or not a navigation is pending
* `page` contains a readonly `{ path, params, query }` object, identical to that passed to `preload` functions
* `session` contains whatever data was seeded on the server. It is a [writable store](https://svelte.dev/tutorial/writable-stores), meaning you can update it with new data (for example, after the user logs in) and your app will be refreshed
### Seeding session data
On the server, you can populate `session` by passing an option to `sapper.middleware`:
```js
// app/server.js
import { Store } from 'svelte/store.js';
// src/server.js
express() // or Polka, or a similar framework
.use(
compression({ threshold: 0 }),
serve('assets'),
authenticationMiddleware(),
sapper.middleware({
store: request => {
return new Store({
user: request.user
});
}
session: (req, res) => ({
user: req.user
})
})
)
.listen(process.env.PORT);
```
In this example, we're using some imaginary `authenticationMiddleware` that creates a `request.user` object based on the user's cookies. (In real life it tends to be a bit more involved — see [express-session](https://github.com/expressjs/session) and [Passport](http://www.passportjs.org/) if you're ready to learn more about sessions and authentication.)
Because we've supplied a `store` option, Sapper creates a new `Store` instance for each new `request`. The data in our store will be used to render the HTML that Sapper responds with.
### On the client
This time around, we're creating a single store that is attached to each page as the user navigates around the app.
```js
import * as sapper from '../__sapper__/client.js';
import { Store } from 'svelte/store.js';
sapper.start({
target: document.querySelector('#sapper'),
store: data => {
// `data` is whatever was in the server-side store
return new Store(data);
}
});
```
> In order to re-use the server-side store data, it must be serializable (using [devalue](https://github.com/Rich-Harris/devalue)) — no functions or custom classes, just built-in JavaScript data types
> Session data must be serializable (using [devalue](https://github.com/Rich-Harris/devalue)) — no functions or custom classes, just built-in JavaScript data types

View File

@@ -4,7 +4,7 @@ title: Prefetching
Sapper uses code splitting to break your app into small chunks (one per route), ensuring fast startup times.
For *dynamic* routes, such as our `src/routes/blog/[slug].html` example, that's not enough. In order to render the blog post, we need to fetch the data for it, and we can't do that until we know what `slug` is. In the worst case, that could cause lag as the browser waits for the data to come back from the server.
For *dynamic* routes, such as our `src/routes/blog/[slug].svelte` example, that's not enough. In order to render the blog post, we need to fetch the data for it, and we can't do that until we know what `slug` is. In the worst case, that could cause lag as the browser waits for the data to come back from the server.
### rel=prefetch

View File

@@ -51,7 +51,7 @@ Because of this, any pages you want to be included in the exported site must be
The basic rule is this: for an app to be exportable, any two users hitting the same page of your app must get the same content from the server. In other words, any app that involves user sessions or authentication is *not* a candidate for `sapper export`.
Note that you can still export apps with dynamic routes, like our `src/routes/blog/[slug].html` example from earlier. `sapper export` will intercept `fetch` requests made inside `preload`, so the data served from `src/routes/blog/[slug].json.js` will also be captured.
Note that you can still export apps with dynamic routes, like our `src/routes/blog/[slug].svelte` example from earlier. `sapper export` will intercept `fetch` requests made inside `preload`, so the data served from `src/routes/blog/[slug].json.js` will also be captured.
### Route conflicts

View File

@@ -7,6 +7,8 @@ Sapper apps run anywhere that supports Node 8 or higher.
### Deploying to Now
> This section relates to Now 1, not Now 2
We can very easily deploy our apps to [Now][]:
```bash
@@ -20,11 +22,11 @@ For other hosting environments, you may need to do `npm run build` yourself.
### Deploying service workers
Sapper makes the Service Worker file (`service-worker.js`) unique by including a timestamp in the source code
Sapper makes the Service Worker file (`service-worker.js`) unique by including a timestamp in the source code
(calculated using `Date.now()`).
In environments where the app is deployed to multiple servers (such as [Now][]), it is advisable to use a
consistent timestamp for all deployments. Otherwise, users may run into issues where the Service Worker
In environments where the app is deployed to multiple servers (such as [Now][]), it is advisable to use a
consistent timestamp for all deployments. Otherwise, users may run into issues where the Service Worker
updates unexpectedly because the app hits server 1, then server 2, and they have slightly different timestamps.
To override Sapper's timestamp, you can use an environment variable (e.g. `SAPPER_TIMESTAMP`) and then modify

View File

@@ -10,18 +10,18 @@ This can be done like so:
// app/server.js
express() // or Polka, or a similar framework
.use(
'/my-base-path', // <!-- add this line
compression({ threshold: 0 }),
serve('assets'),
sapper.middleware()
)
.listen(process.env.PORT);
.use(
'/my-base-path', // <!-- add this line
compression({ threshold: 0 }),
serve('assets'),
sapper.middleware()
)
.listen(process.env.PORT);
```
Sapper will detect the base path and configure both the server-side and client-side routers accordingly.
If you're [exporting](guide#exporting) your app, you will need to tell the exporter where to begin crawling:
If you're [exporting](docs#exporting) your app, you will need to tell the exporter where to begin crawling:
```bash
sapper export --basepath my-base-path

View File

@@ -292,7 +292,7 @@ Once your `App.html` has been created and your server and client apps updated, y
##### app/template.html
* Your `<head>` element must contain `%sapper.base%` (see ([base URLs](guide#base-urls))
* Your `<head>` element must contain `%sapper.base%` (see ([base URLs](docs#base-urls))
* Remove references to your service worker; this is now handled by `%sapper.scripts%`
##### Pages