sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
7k stars 434 forks source link

Optimise apps with only one route #444

Open Rich-Harris opened 6 years ago

Rich-Harris commented 6 years ago

This has been a long term goal...

screen shot 2018-09-19 at 7 29 13 pm

...but Guillermo's tweet has got me thinking about what concrete steps we can take in that direction.

One is to avoid hydrating static routes, and in fact that's one of the oldest issues on this repo: https://github.com/sveltejs/sapper/issues/8

But another, simpler step we can take is to optimise for the (not that uncommon!) case where you only have a single route. In that case, we don't need a router! Which means we don't need sapper/runtime.js.

Ideally the user wouldn't need to think about this. That means that we'd need to change this...

import { init } from 'sapper/runtime.js';
import { manifest } from './manifest/client.js';

init({
  target: document.querySelector('#sapper'),
  manifest
});

...to something like this:

import * as app from './manifest/client.js';

app.init({
  target: document.querySelector('#sapper')
});

In the case where the app is completely static (i.e. no interactivity in the src/routes/index.html component), the manifest file could be as simple as this:

// src/manifest/client.js
export function init(){} // noop

I'm glossing over a few things here. What becomes of goto and prefetchRoutes etc — do they also become part of src/manifest/client.js instead of sapper/runtime.js (and if so, does it still make sense to call it a 'manifest')?

Details aside, this feels like a promising direction that would make it a complete no-brainer to use Sapper for projects of all sizes.

Rich-Harris commented 6 years ago

Was just thinking about https://github.com/sveltejs/sapper/issues/445 in the context of this issue, and it occurred to me that there's a neat approach to all of this: we get rid of the Sapper middleware and sapper/runtime.js.

Instead, that logic goes into the generated files, not unlike the way Svelte dissolves itself into a compiled component.

Here's what I'm thinking:

// app/client.js
-import { init } from 'sapper/runtime.js';
-import { manifest } from './manifest/client.js';
+import * as sapper from './__sapper__/client.js';

sapper.init({
- manifest,
  target: document.querySelector('#sapper')
});
// app/server.js
import sirv from 'sirv';
import polka from 'polka';
-import sapper from 'sapper';
import compression from 'compression';
-import { manifest } from './manifest/server.js';
+import * as sapper from './__sapper__/server.js';

const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';

polka() // You can also use Express
  .use(
    compression({ threshold: 0 }),
    sirv('static', { dev }),
-   sapper({ manifest })
+   sapper.middleware()
  )
  .listen(PORT, err => {
    if (err) console.log('error', err);
  });
// app/service-worker.js
-import { timestamp, assets, shell, routes } from './manifest/service-worker.js';
+import { timestamp, files, shell, routes } from './__sapper__/service-worker.js';

// ...

'Manifest' is an odd name for a collection of files that export functions as well as data, so I'm proposing that it be renamed to __sapper__, which a) is more obviously a directory of generated files that probably shouldn't be checked in to version control, and b) will be the first item in the src directory (I find it weird when manifest is the second or third directory, nestled between e.g. src/components and src/routes).

I find this setup more logical. It's much clearer that src/__sapper__/client.js contains stuff that shouldn't run on the server (unlike sapper/runtime.js), it avoids the weirdness of importing a manifest only to pass it back to the same framework that generated it, and it gives us the ability to pull off neat tricks like making an empty app as shown above.

Any thoughts?

arxpoetica commented 6 years ago

b) will be the first item in the src directory

Yes please! Sign me up!

Rich-Harris commented 6 years ago

@mrkishi pointed out on Discord that it's odd to have generated files in src at all, and perhaps it should be project/__sapper__ instead of project/src/__sapper__.

This would mean that the project root would contain .sapper and __sapper__ (.sapper is where the output of webpack/Rollup ends up during sapper dev or sapper export). That also doesn't seem ideal. I don't think that __sapper__ should be used for both input and output. Not sure what the solution is.