mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-12 03:05:12 +00:00
90 lines
3.1 KiB
Markdown
90 lines
3.1 KiB
Markdown
# sapper
|
|
|
|
Combat-ready apps, engineered by Svelte.
|
|
|
|
## This is not a thing yet
|
|
|
|
If you visit this README in a few weeks, hopefully it will have blossomed into the app development framework we deserve. Right now, it's just a set of ideas.
|
|
|
|
---
|
|
|
|
[Next.js](https://github.com/zeit/next.js/) introduced a beautiful idea — that you should be able to build your app as universal React components in a special `pages` directory, and the framework should take care of routing and rendering on both client and server. What if we did the same thing for Svelte?
|
|
|
|
High-level goals:
|
|
|
|
* Extreme ease of development
|
|
* Code-splitting and HMR out of the box (probably via webpack)
|
|
* Best-in-class performance
|
|
* As little magic as possible. Anyone should be able to understand how everything fits together, and e.g. make changes to the webpack config
|
|
|
|
|
|
## Design
|
|
|
|
A Sapper app is just an Express app (conventionally, `server.js`) that uses the `sapper` middleware:
|
|
|
|
```js
|
|
const app = require('express')();
|
|
const sapper = require('sapper');
|
|
|
|
const app = express();
|
|
|
|
app.use(sapper());
|
|
|
|
const { PORT = 3000 } = process.env;
|
|
app.listen(PORT, () => {
|
|
console.log(`listening on port ${PORT}`);
|
|
});
|
|
```
|
|
|
|
The middleware serves pages that match files in the `routes` directory, and assets generated by webpack. In development mode, the middleware once activated watches `routes` to keep the app up-to-date.
|
|
|
|
|
|
## Routing
|
|
|
|
Like Next, routes are defined by the project directory structure, but with some crucial differences:
|
|
|
|
* Files with an `.html` extension are treated as Svelte components. The `routes/about.html` (or `routes/about/index.html`) would create the `/about` route.
|
|
* Files with a `.js` or `.mjs` extension are more generic route handlers. These files should export functions corresponding to the HTTP methods they support (example below).
|
|
* Instead of route masking, we embed parameters in the filename. For example `post/%id%.html` maps to `/post/:id`, and the component will be rendered with the appropriate parameter.
|
|
* Nested routes (read [this article](https://joshduff.com/2015-06-why-you-need-a-state-router.md)) can be handled by creating a file that matches the subroute — for example, `routes/app/settings/%submenu%.html` would match `/app/settings/profile` *and* `app/settings`, but in the latter case the `submenu` parameter would be `null`.
|
|
|
|
An example of a generic route:
|
|
|
|
```js
|
|
// routes/api/post/%id%.js
|
|
export async function get(req, res) {
|
|
try {
|
|
const data = await getPostFromDatabase(req.params.id);
|
|
const json = JSON.stringify(data);
|
|
|
|
res.set({
|
|
'Content-Type': 'application/json',
|
|
'Content-Length': json.length
|
|
});
|
|
|
|
res.send(json);
|
|
} catch (err) {
|
|
res.status(500).send(err.message);
|
|
}
|
|
}
|
|
```
|
|
|
|
Or, if you omit the `res` argument, it can use the return value:
|
|
|
|
```js
|
|
// routes/api/post/%id%.js
|
|
export async function get(req, res) {
|
|
return await getPostFromDatabase(req.params.id);
|
|
}
|
|
```
|
|
|
|
|
|
## Things to figure out
|
|
|
|
* How to customise the overall page template
|
|
* An equivalent of `getInitialProps`
|
|
* Critical CSS
|
|
* `store` integration
|
|
* Route transitions
|
|
* Equivalent of `next export`
|
|
* ...and lots of other things that haven't occurred to me yet. |