{a} + {b} = {a + b}
97 | ``` 98 | 99 | You can see how in the `p` method (which stands for uPdate — I know...) the compiler is able to selectively update text nodes based on whether `a` has changed, `b` has changed, or in the case of `{a + b}` whether *either* `a` or `b` has changed, using **bitwise operators**. It looks like overkill for a simple example like this, but this granular change-checking is how Svelte is able to avoid re-rendering entire components when state changes. -------------------------------------------------------------------------------- /content/04-starting-from-scratch.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Starting from scratch 3 | --- 4 | 5 | You can build apps inside the [Svelte REPL](https://svelte.dev/repl), but at some point you'll want to develop in your local environment. 6 | 7 | The easiest way is to use [degit](https://github.com/Rich-Harris/degit), a git-based scaffolding tool... 8 | 9 | ```bash 10 | mkdir my-project 11 | cd my-project 12 | npx degit sveltejs/template 13 | ``` 14 | 15 | ...but it's helpful to understand how things work under the hood, so we're going to create a project from nothing. 16 | 17 | Create a new folder and add a `src` folder inside: 18 | 19 | ```bash 20 | mkdir my-project 21 | cd my-project 22 | 23 | mkdir src 24 | ``` 25 | 26 | 27 | ## Installing Rollup 28 | 29 | As of June 2020, the default template uses [Rollup](http://rollupjs.org) as its module bundler, though there is also an official [webpack loader](https://github.com/sveltejs/svelte-loader) and a plethora of third party integrations for Parcel, Snowpack, Vite and everything else you might want to use. Install Rollup... 30 | 31 | ```bash 32 | npm i -D rollup 33 | ``` 34 | 35 | ...and create a `rollup.config.js` file: 36 | 37 | ```js 38 | export default { 39 | input: 'src/main.js', 40 | output: { 41 | file: 'public/build/bundle.js', 42 | format: 'esm', 43 | sourcemap: true 44 | } 45 | }; 46 | ``` 47 | 48 | Now we need to create that `src/main.js` entry point. We'll also create a test module to import, so you can see what Rollup does: 49 | 50 | ```bash 51 | echo "import hello from './hello.js'; 52 | 53 | hello();" > src/main.js 54 | 55 | echo "export default () => { 56 | console.log('hello!'); 57 | }" > src/hello.js 58 | ``` 59 | 60 | Run Rollup with the config file... 61 | 62 | ```bash 63 | npx rollup -c 64 | ``` 65 | 66 | ...and look at the `public/build/bundle.js` file: 67 | 68 | ```js 69 | var hello = () => { 70 | console.log('hello!'); 71 | }; 72 | 73 | hello(); 74 | //# sourceMappingURL=bundle.js.map 75 | ``` 76 | 77 | Now we just need to add a `public/index.html` file to load the bundle... 78 | 79 | ```html 80 | 81 | 82 | 83 | 84 | 85 | 86 |${html}
`;
77 | };
78 |
79 | renderer.heading = (text, level, title) => {
80 | const slug = level <= 4 && make_slug(title);
81 |
82 | subsections.push({ slug, title });
83 |
84 | return `
85 | Next: {section.next.title}
29 | {/if} 30 | 31 | -------------------------------------------------------------------------------- /src/routes/_error.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 |{error.message}
37 | 38 | {#if dev && error.stack} 39 |{error.stack}40 | {/if} 41 | -------------------------------------------------------------------------------- /src/routes/_layout.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 20 |
This is the 'about' page. There's not much here.
-------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/routes/toc.json.js: -------------------------------------------------------------------------------- 1 | import { get_sections } from '@app/get_sections.js'; 2 | 3 | export async function get(req, res) { 4 | const toc = get_sections().map(({ html, ...section }) => section); 5 | 6 | res.setHeader('Content-Type', 'application/json'); 7 | res.end(JSON.stringify(toc)); 8 | } 9 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import sirv from 'sirv'; 2 | import polka from 'polka'; 3 | import compression from 'compression'; 4 | import * as sapper from '@sapper/server'; 5 | 6 | const { PORT, NODE_ENV } = process.env; 7 | const dev = NODE_ENV === 'development'; 8 | 9 | polka() // You can also use Express 10 | .use( 11 | compression({ threshold: 0 }), 12 | sirv('static', { dev }), 13 | sapper.middleware() 14 | ) 15 | .listen(PORT, err => { 16 | if (err) console.log('error', err); 17 | }); 18 | -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | import { timestamp, files, shell, routes } from '@sapper/service-worker'; 2 | 3 | const ASSETS = `cache${timestamp}`; 4 | 5 | // `shell` is an array of all the files generated by the bundler, 6 | // `files` is an array of everything in the `static` directory 7 | const to_cache = shell.concat(files); 8 | const cached = new Set(to_cache); 9 | 10 | self.addEventListener('install', event => { 11 | event.waitUntil( 12 | caches 13 | .open(ASSETS) 14 | .then(cache => cache.addAll(to_cache)) 15 | .then(() => { 16 | self.skipWaiting(); 17 | }) 18 | ); 19 | }); 20 | 21 | self.addEventListener('activate', event => { 22 | event.waitUntil( 23 | caches.keys().then(async keys => { 24 | // delete old caches 25 | for (const key of keys) { 26 | if (key !== ASSETS) await caches.delete(key); 27 | } 28 | 29 | self.clients.claim(); 30 | }) 31 | ); 32 | }); 33 | 34 | self.addEventListener('fetch', event => { 35 | if (event.request.method !== 'GET' || event.request.headers.has('range')) return; 36 | 37 | const url = new URL(event.request.url); 38 | 39 | // don't try to handle e.g. data: URIs 40 | if (!url.protocol.startsWith('http')) return; 41 | 42 | // ignore dev server requests 43 | if (url.hostname === self.location.hostname && url.port !== self.location.port) return; 44 | 45 | // always serve static files and bundler-generated assets from cache 46 | if (url.host === self.location.host && cached.has(url.pathname)) { 47 | event.respondWith(caches.match(event.request)); 48 | return; 49 | } 50 | 51 | // for pages, you might want to serve a shell `service-worker-index.html` file, 52 | // which Sapper has generated for you. It's not right for every 53 | // app, but if it's right for yours then uncomment this section 54 | /* 55 | if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) { 56 | event.respondWith(caches.match('/service-worker-index.html')); 57 | return; 58 | } 59 | */ 60 | 61 | if (event.request.cache === 'only-if-cached') return; 62 | 63 | // for everything else, try the network first, falling back to 64 | // cache if the user is offline. (If the pages never change, you 65 | // might prefer a cache-first approach to a network-first one.) 66 | event.respondWith( 67 | caches 68 | .open(`offline${timestamp}`) 69 | .then(async cache => { 70 | try { 71 | const response = await fetch(event.request); 72 | cache.put(event.request, response.clone()); 73 | return response; 74 | } catch(err) { 75 | const response = await cache.match(event.request); 76 | if (response) return response; 77 | 78 | throw err; 79 | } 80 | }) 81 | ); 82 | }); 83 | -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %sapper.base% 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | %sapper.styles% 19 | 20 | 22 | %sapper.head% 23 | 24 | 25 | 27 |