Svelte is awesome due to its simplicity, but it also can be abused such that it becomes unreadable. This repo houses some design standards to abide by for simpler and readable svelte.
Lets just go by the order of script
, html
stuff, then style
at the end of the file. This is mostly arbitrary, but being consistent will be nice.
For example
<script>
// js
</script>
<!-- html -->
<style>
/* css */
</style>
Within the <script>
tag, roughly organize the code in the following order:
const
or export const
)export let
)let
)$:
)onMount
, onDestroy
, etc.)For example
<script>
// 1.1 CSS imports
import "app.css";
// 1.2 Svelte components
import Scatterplot from "../components/Scatterplot.svelte";
import Slider from "../components/Slider.svelte";
// 1.3 Code imports
import { onMount } from "svelte";
// 2. constants
const name = "Donny";
// 3. props
export let height = 73;
// 4. mutable variables
let age = 20;
// 5. reactive statements
$: dogYears = humanToDogYears(age);
// 6. function calls
onMount(() => {
age = birthday(age);
printPerson();
});
// 7. function definitions
function printPerson() {
console.log(name, age);
}
function birthday(age) {
const newAge = age + 1;
return newAge;
}
function humanToDogYears(age) {
return age * 7;
}
</script>
In general, svelte projects get confusing when the functions start to take parameters and mutate global variables.
If possible, try not to mutate global variables within function definitions.
Warning Try to avoid this below if you can
<script>
let age = 20;
// mutates global variable age
function birthday() {
age++;
}
// when birthday needs to be called (could be anywhere)
birthday();
</script>
Note Try this below instead
<script>
let age = 20;
// takes in age as a parameter and returns the new age
// then, elsewhere redefine age as the new age
function birthday(age) {
const newAge = age + 1;
return newAge;
}
// when birthday needs to be called (could be anywhere)
const newAge = birthday(age);
age = newAge; // and then do reassignment to the global
</script>
then you get the benefit of
In my experience, the $: {}
parts always lead to the most confusion to what is actually happening.
When using $: {}
it can be hard to know
To clear this up, lets try to use the $:
statements only and do more complex operations within a function definition instead of a $: {}
statement.
For example,
Warning Try to avoid this below if you can
<script>
let dogAge, chickenAge, cowAge;
$: {
cowAge = age * 3.64;
chickenAge = age * 5.33;
dogAge = age * 7;
}
</script>
Note Try this below instead
<script>
$: dogAge = humanToDogYears(age);
$: chickenAge = humanToChickenYears(age);
$: cowAge = humanToCowYears(age);
function humanToDogYears(age) {
return age * 7;
}
function humanToChickenYears(age) {
return age * 5.33;
}
function humanToCowYears(age) {
return age * 3.64;
}
</script>
The parameters of the function should always be why the $:
should react / be reran. Now it should be clear what the reactive statement is doing (from function name) and why its reacting (from function parameters).
This also allows you to ignore variables that should not rerun the $:
In the example below, I only redraw points when the points array changes or when canvasEl changes. I don't react to point size
changes. If this were all done in a $: {}
statement, then it would rerun every time size
changes.
<script>
export let points = [
{ x: 1, y: 2 },
{ x: 2, y: 3 },
{ x: 3, y: 4 },
];
export let size = 3;
let canvasEl;
$: redrawPoints(canvasEl, points);
function redrawPoints(canvasEl, points) {
if (canvasEl) {
const ctx = canvasEl.getContext("2d");
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
points.forEach((point) => {
ctx.fillRect(point.x, point.y, size, size);
});
}
}
</script>
<canvas width="{100}" height="{100}" bind:this="{canvasEl}"></canvas>
But now you see! You get control over reactive statements and readability by not using anonymous statements $: {}
and instead using function definitions for english readability and control over what variables should trigger the function.
$:
should not be anonymouscreateEventDispatcher
and dispatch
(especially for ts)