Skip to content

Commit

Permalink
replace router/hydrate options with csr (#6446)
Browse files Browse the repository at this point in the history
* replace router/hydrate options with csr - closes #6436

* update template

* update docs

* fail noisily
  • Loading branch information
Rich-Harris authored Aug 30, 2022
1 parent 60d44b6 commit f1f8d33
Show file tree
Hide file tree
Showing 34 changed files with 120 additions and 232 deletions.
5 changes: 5 additions & 0 deletions .changeset/sharp-glasses-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] replace `router`/`hydrate` page options with `csr`
2 changes: 1 addition & 1 deletion documentation/docs/00-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ title: Introduction

SvelteKit is a framework for building extremely high-performance web apps.

Building an app with all the modern best practices is fiendishly complicated. Those practices include [build optimizations](https://vitejs.dev/guide/features.html#build-optimizations), so that you load only the minimal required code; [offline support](/docs/service-workers); [prefetching](/docs/a-options#sveltekit-prefetch) pages before the user initiates navigation; and [configurable rendering](/docs/page-options) that allows you to generate HTML [on the server](/docs/appendix#ssr) or [in the browser](/docs/page-options#router) at runtime or [at build-time](/docs/page-options#prerender). SvelteKit does all the boring stuff for you so that you can get on with the creative part.
Building an app with all the modern best practices is fiendishly complicated. Those practices include [build optimizations](https://vitejs.dev/guide/features.html#build-optimizations), so that you load only the minimal required code; [offline support](/docs/service-workers); [prefetching](/docs/a-options#sveltekit-prefetch) pages before the user initiates navigation; and [configurable rendering](/docs/page-options) that allows you to render your app [on the server](/docs/appendix#ssr) or [in the browser](/docs/appendix#csr) at runtime or [at build-time](/docs/page-options#prerender). SvelteKit does all the boring stuff for you so that you can get on with the creative part.

It uses [Vite](https://vitejs.dev/) with a [Svelte plugin](https://github.com/sveltejs/vite-plugin-svelte) to provide a lightning-fast and feature-rich development experience with [Hot Module Replacement (HMR)](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#hot), where changes to your code are reflected in the browser instantly.

Expand Down
9 changes: 4 additions & 5 deletions documentation/docs/03-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ This function runs alongside `+page.svelte`, which means it runs on the server d
As well as `load`, `page.js` can export values that configure the page's behaviour:

- `export const prerender = true` or `false` or `'auto'`
- `export const hydrate = true` or `false`
- `export const router = true` or `false`
- `export const ssr = true` or `false`
- `export const csr = true` or `false`

You can find more information about these in [page options](/docs/page-options).

Expand Down Expand Up @@ -111,7 +110,7 @@ export async function load({ params }) {

During client-side navigation, SvelteKit will load this data from the server, which means that the returned value must be serializable using [devalue](https://github.com/rich-harris/devalue).

Like `+page.js`, `+page.server.js` can export [page options](/docs/page-options)`prerender`, `hydrate`, `router` and `ssr`.
Like `+page.js`, `+page.server.js` can export [page options](/docs/page-options)`prerender`, `ssr` and `csr`.

#### Actions

Expand Down Expand Up @@ -282,7 +281,7 @@ export function load() {
}
```
If a `+layout.js` exports [page options](/docs/page-options) — `prerender`, `hydrate` `router` and `ssr` — they will be used as defaults for child pages.
If a `+layout.js` exports [page options](/docs/page-options) — `prerender`, `ssr` and `csr` — they will be used as defaults for child pages.
Data returned from a layout's `load` function is also available to all its child pages:
Expand All @@ -302,7 +301,7 @@ Data returned from a layout's `load` function is also available to all its child
To run your layout's `load` function on the server, move it to `+layout.server.js`, and change the `LayoutLoad` type to `LayoutServerLoad`.
Like `+layout.js`, `+layout.server.js` can export [page options](/docs/page-options) — `prerender`, `hydrate` `router` and `ssr`.
Like `+layout.js`, `+layout.server.js` can export [page options](/docs/page-options) — `prerender`, `ssr` and `csr`.
### +server
Expand Down
2 changes: 0 additions & 2 deletions documentation/docs/09-a-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ We can mitigate that by _prefetching_ the data. Adding a `data-sveltekit-prefetc

...will cause SvelteKit to run the page's `load` function as soon as the user hovers over the link (on a desktop) or touches it (on mobile), rather than waiting for the `click` event to trigger navigation. Typically, this buys us an extra couple of hundred milliseconds, which is the difference between a user interface that feels laggy, and one that feels snappy.

Note that prefetching will not work if the [`router`](/docs/page-options#router) setting is `false`.

You can also programmatically invoke `prefetch` from `$app/navigation`.

### data-sveltekit-reload
Expand Down
29 changes: 9 additions & 20 deletions documentation/docs/12-page-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,35 +68,24 @@ For that reason among others, it's recommended that you always include a file ex

For _pages_, we skirt around this problem by writing `foo/index.html` instead of `foo`.

### hydrate

Ordinarily, SvelteKit [hydrates](/docs/appendix#hydration) your server-rendered HTML into an interactive page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can skip hydration through the `hydrate` export:

```js
/// file: +page.js
export const hydrate = false;
```

> If `hydrate` and `router` are both `false`, SvelteKit will not add any JavaScript to the page at all. If [server-side rendering](/docs/hooks#handle) is disabled in `handle`, `hydrate` must be `true` or no content will be rendered.
### router
Note that this will disable client-side routing for any navigation from this page, regardless of whether the router is already active.

SvelteKit includes a [client-side router](/docs/appendix#routing) that intercepts navigations (from the user clicking on links, or interacting with the back/forward buttons) and updates the page contents, rather than letting the browser handle the navigation by reloading.
### ssr

In certain circumstances you might need to disable [client-side routing](/docs/appendix#routing) through the `router` export:
Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it's hydrated. If you set `ssr` to `false`, it renders an empty 'shell' page instead. This is useful if your page is unable to be rendered on the server, but in most situations it's not recommended ([see appendix](/docs/appendix#ssr)).

```js
/// file: +page.js
export const router = false;
export const ssr = false;
```

Note that this will disable client-side routing for any navigation from this page, regardless of whether the router is already active.
### csr

### ssr

Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it's hydrated. If you set `ssr` to `false`, it renders an empty 'shell' page instead. This is useful if your page accesses browser-only methods or objects, but in most situations it's not recommended ([see appendix](/docs/appendix#ssr)).
Ordinarily, SvelteKit [hydrates](/docs/appendix#hydration) your server-rendered HTML into an interactive client-side-rendered (CSR) page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can disable CSR:

```js
/// file: +page.js
export const ssr = false;
export const csr = false;
```

> If both `ssr` and `csr` are `false`, nothing will be rendered!
7 changes: 2 additions & 5 deletions documentation/docs/17-seo.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,11 @@ const config = {
export default config;
```

...disabling `hydrate` and `router` in your root `+layout.js`/`+layout.server.js`...
...disabling `csr` in your root `+layout.js`/`+layout.server.js`...

```js
/// file: src/routes/+layout.server.js
// the combination of these options
// disables JavaScript
export const hydrate = false;
export const router = false;
export const csr = false;
```

...and transforming the HTML using `transformPageChunk` along with `transform` imported from `@sveltejs/amp`:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { browser, dev } from '$app/environment';
import { dev } from '$app/environment';

// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement...
export const hydrate = dev;

// ...but if the client-side router is already loaded
// (i.e. we came here from elsewhere in the app), use it
export const router = browser;
export const csr = dev;

// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in prod
Expand Down
53 changes: 16 additions & 37 deletions packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ export function create_client({ target, base, trailing_slash }) {
/** @type {import('svelte').SvelteComponent} */
let root;

let router_enabled = true;

// keeping track of the history index in order to prevent popstate navigation events if needed
let current_history_index = history.state?.[INDEX_KEY];

Expand Down Expand Up @@ -155,22 +153,18 @@ export function create_client({ target, base, trailing_slash }) {
url = new URL(url, get_base_uri(document));
}

if (router_enabled) {
return navigate({
url,
scroll: noscroll ? scroll_state() : null,
keepfocus,
redirect_chain,
details: {
state,
replaceState
},
accepted: () => {},
blocked: () => {}
});
}

await native_navigation(url);
return navigate({
url,
scroll: noscroll ? scroll_state() : null,
keepfocus,
redirect_chain,
details: {
state,
replaceState
},
accepted: () => {},
blocked: () => {}
});
}

/** @param {URL} url */
Expand Down Expand Up @@ -241,15 +235,7 @@ export function create_client({ target, base, trailing_slash }) {
routeId: null
});
} else {
if (router_enabled) {
goto(new URL(navigation_result.location, url).href, {}, [
...redirect_chain,
url.pathname
]);
} else {
await native_navigation(new URL(navigation_result.location, location.href));
}

goto(new URL(navigation_result.location, url).href, {}, [...redirect_chain, url.pathname]);
return false;
}
} else if (navigation_result.props?.page?.status >= 400) {
Expand Down Expand Up @@ -354,9 +340,6 @@ export function create_client({ target, base, trailing_slash }) {
page = navigation_result.props.page;
}

const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1];
router_enabled = leaf_node?.node.shared?.router !== false;

if (callback) callback();

updating = false;
Expand Down Expand Up @@ -398,10 +381,8 @@ export function create_client({ target, base, trailing_slash }) {
});
}

if (router_enabled) {
const navigation = { from: null, to: new URL(location.href) };
callbacks.after_navigate.forEach((fn) => fn(navigation));
}
const navigation = { from: null, to: new URL(location.href) };
callbacks.after_navigate.forEach((fn) => fn(navigation));

started = true;
}
Expand Down Expand Up @@ -1185,8 +1166,6 @@ export function create_client({ target, base, trailing_slash }) {

/** @param {MouseEvent} event */
addEventListener('click', (event) => {
if (!router_enabled) return;

// Adapted from https://github.com/visionmedia/page.js
// MIT license https://github.com/visionmedia/page.js#license
if (event.button || event.which !== 1) return;
Expand Down Expand Up @@ -1256,7 +1235,7 @@ export function create_client({ target, base, trailing_slash }) {
});

addEventListener('popstate', (event) => {
if (event.state && router_enabled) {
if (event.state) {
// if a popstate-driven navigation is cancelled, we need to counteract it
// with history.go, which means we end up back here, hence this check
if (event.state[INDEX_KEY] === current_history_index) return;
Expand Down
31 changes: 15 additions & 16 deletions packages/kit/src/runtime/client/start.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import { create_client } from './client.js';
import { init } from './singletons.js';
import { set_paths } from '../paths.js';

export { set_public_env } from '../env-public.js';
import { set_public_env } from '../env-public.js';

/**
* @param {{
* paths: {
* assets: string;
* base: string;
* },
* target: Element;
* route: boolean;
* spa: boolean;
* trailing_slash: import('types').TrailingSlash;
* env: Record<string, string>;
* hydrate: {
* status: number;
* error: Error | (import('../server/page/types').SerializedHttpError);
Expand All @@ -23,26 +15,33 @@ export { set_public_env } from '../env-public.js';
* data: Array<import('types').ServerDataNode | null>;
* errors: Record<string, any> | null;
* };
* paths: {
* assets: string;
* base: string;
* },
* target: Element;
* trailing_slash: import('types').TrailingSlash;
* }} opts
*/
export async function start({ paths, target, route, spa, trailing_slash, hydrate }) {
export async function start({ env, hydrate, paths, target, trailing_slash }) {
set_public_env(env);
set_paths(paths);

const client = create_client({
target,
base: paths.base,
trailing_slash
});

init({ client });
set_paths(paths);

if (hydrate) {
await client._hydrate(hydrate);
} else {
client.goto(location.href, { replaceState: true });
}

if (route) {
if (spa) client.goto(location.href, { replaceState: true });
client._start_router();
}
client._start_router();

dispatchEvent(new CustomEvent('sveltekit:start'));
}
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export async function respond(request, options, state) {
event,
options,
state,
page_config: { router: true, hydrate: true, ssr: false },
page_config: { ssr: false, csr: true },
status: 200,
error: null,
branch: [],
Expand Down
10 changes: 4 additions & 6 deletions packages/kit/src/runtime/server/page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,8 @@ export async function render_page(event, route, page, options, state, resolve_op
fetched,
cookies,
page_config: {
hydrate: true,
router: true,
ssr: false
ssr: false,
csr: get_option(nodes, 'csr') ?? true
},
status,
error: null,
Expand Down Expand Up @@ -273,7 +272,7 @@ export async function render_page(event, route, page, options, state, resolve_op
options,
state,
resolve_opts,
page_config: { router: true, hydrate: true, ssr: true },
page_config: { ssr: true, csr: true },
status,
error,
branch: compact(branch.slice(0, j + 1)).concat({
Expand Down Expand Up @@ -323,8 +322,7 @@ export async function render_page(event, route, page, options, state, resolve_op
state,
resolve_opts,
page_config: {
router: get_option(nodes, 'router') ?? true,
hydrate: get_option(nodes, 'hydrate') ?? true,
csr: get_option(nodes, 'csr') ?? true,
ssr: true
},
status,
Expand Down
Loading

0 comments on commit f1f8d33

Please sign in to comment.