Skip to content

Commit 86da212

Browse files
committed
use detail elements for "why we did this"
1 parent 8507d9f commit 86da212

File tree

1 file changed

+58
-63
lines changed

1 file changed

+58
-63
lines changed

apps/svelte.dev/content/docs/svelte/05-misc/07-v5-migration-guide.md

Lines changed: 58 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ In Svelte 4, a `let` declaration at the top level of a component was implicitly
2222

2323
Nothing else changes. `count` is still the number itself, and you read and write directly to it, without a wrapper like `.value` or `getCount()`.
2424

25-
#### Why we did this
26-
27-
`let` being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - a `let` declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn't as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API in an outside the top level of components. Head to TODO LINK TO TUTORIAL to learn more.
25+
> [!DETAILS] Why we did this
26+
> `let` being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - a `let` declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn't as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API in an outside the top level of components. Head to TODO LINK TO TUTORIAL to learn more.
2827
2928
### $: -> $derived/$effect
3029

@@ -52,26 +51,25 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is
5251
</script>
5352
```
5453

55-
#### Why we did this
56-
57-
`$:` was a great shorthand and easy to get started with: you could slap a `$:` in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn't as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With `$derived` and `$effect`, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want `$derived`), but future-you and other developers on your team will have an easier time.
58-
59-
There were also gotchas that were hard to spot:
60-
61-
- `$:` only updated directly before rendering, which meant you could read stale values in-between rerenders
62-
- `$:` only ran once per tick, which meant that statements may run less often than you think
63-
- `$:` dependencies were determined through static analysis of the dependencies. This worked in most cases, but could break in subtle ways during a refactoring where dependencies would be for example moved into a function and no longer be visible as a result
64-
- `$:` statements were also ordered by using static analysis of the dependencies. In some cases there could be ties and the ordering would be wrong as a result, needing manual interventions. Ordering could also break while refactoring code and some dependencies no longer being visible as a result.
65-
66-
Lastly, it wasn't TypeScript-friendly (our editor tooling had to jump through some hoops to make it valid for TypeScript), which was a blocker for making Svelte's reactivity model truly universal.
67-
68-
`$derived` and `$effect` fix all of these by
69-
70-
- always returning the latest value
71-
- running as often as needed to be stable
72-
- determining the dependencies at runtime, and therefore being immune to refactorings
73-
- executing dependencies as needed and therefore being immune to ordering problems
74-
- being TypeScript-friendly
54+
> [!DETAILS] Why we did this
55+
> `$:` was a great shorthand and easy to get started with: you could slap a `$:` in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn't as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With `$derived` and `$effect`, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want `$derived`), but future-you and other developers on your team will have an easier time.
56+
>
57+
> There were also gotchas that were hard to spot:
58+
>
59+
> - `$:` only updated directly before rendering, which meant you could read stale values in-between rerenders
60+
> - `$:` only ran once per tick, which meant that statements may run less often than you think
61+
> - `$:` dependencies were determined through static analysis of the dependencies. This worked in most cases, but could break in subtle ways during a refactoring where dependencies would be > for example moved into a function and no longer be visible as a result
62+
> - `$:` statements were also ordered by using static analysis of the dependencies. In some cases there could be ties and the ordering would be wrong as a result, needing manual > interventions. Ordering could also break while refactoring code and some dependencies no longer being visible as a result.
63+
>
64+
> Lastly, it wasn't TypeScript-friendly (our editor tooling had to jump through some hoops to make it valid for TypeScript), which was a blocker for making Svelte's reactivity model truly > universal.
65+
>
66+
> `$derived` and `$effect` fix all of these by
67+
>
68+
> - always returning the latest value
69+
> - running as often as needed to be stable
70+
> - determining the dependencies at runtime, and therefore being immune to refactorings
71+
> - executing dependencies as needed and therefore being immune to ordering problems
72+
> - being TypeScript-friendly
7573
7674
### export let -> $props
7775

@@ -112,11 +110,10 @@ In Svelte 5, the `$props` rune makes this straightforward without any additional
112110
<button {class} {...---$$restProps---+++rest+++}>click me</button>
113111
```
114112

115-
#### Why we did this
116-
117-
`export let` was one of the more controversial API decisions, and there was a lot of debate about whether you should think about a property being `export`ed or `import`ed. `$props` doesn't have this trait. It's also in line with the other runes, and the general thinking reduces to "everything special to reactivity in Svelte is a rune".
118-
119-
There were also a lot of limitations around `export let`, which required additional API, as shown above. `$props` unite this in one syntactical concept that leans heavily on regular JavaScript destructuring syntax.
113+
> [!DETAILS] Why we did this
114+
> `export let` was one of the more controversial API decisions, and there was a lot of debate about whether you should think about a property being `export`ed or `import`ed. `$props` doesn't have this trait. It's also in line with the other runes, and the general thinking reduces to "everything special to reactivity in Svelte is a rune".
115+
>
116+
> There were also a lot of limitations around `export let`, which required additional API, as shown above. `$props` unite this in one syntactical concept that leans heavily on regular JavaScript destructuring syntax.
120117
121118
## Event changes
122119

@@ -314,30 +311,29 @@ When spreading props, local event handlers must go _after_ the spread, or they r
314311
</button>
315312
```
316313

317-
### Why we did this
318-
319-
`createEventDispatcher` was always a bit boilerplate-y:
320-
321-
- import the function
322-
- call the function to get a dispatch function
323-
- call said dispatch function with a string and possibly a payload
324-
- retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent`
325-
326-
It was always possible to use component callback props, but because you had to listen to dom events using `on:`, it made sense to use `createEventDispatcher` for component events due to syntactical consiscency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do.
327-
328-
The removal of event modifiers is arguably one of the changes that seems like a step back for those who've liked the shorthand syntax of event modifiers. Given that they are not used that frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on Dom elements.
329-
330-
Multiple listeners for the same event are also no longer possible, but it was something of an anti-pattern anyway, since it impedes readability: if there are many attributes, it becomes harder to spot that there are two handlers unless they are right next to each other. It also implies that the two handlers are independent, when in fact something like `event.stopImmediatePropagation()` inside `one` would prevent `two` from being called.
331-
332-
By deprecating `createEventDispatcher` and the `on:` directive in favour of callback props and normal element properties, we:
333-
334-
- reduce Svelte's learning curve
335-
- remove boilerplate, particularly around `createEventDispatcher`
336-
- remove the overhead of creating `CustomEvent` objects for events that may not even have listeners
337-
- add the ability to spread event handlers
338-
- add the ability to know which event handlers were provided to a component
339-
- add the ability to express whether a given event handler is required or optional
340-
- increase type safety (previously, it was effectively impossible for Svelte to guarantee that a component didn't emit a particular event)
314+
> [!DETAILS] Why we did this
315+
> `createEventDispatcher` was always a bit boilerplate-y:
316+
>
317+
> - import the function
318+
> - call the function to get a dispatch function
319+
> - call said dispatch function with a string and possibly a payload
320+
> - retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent`
321+
>
322+
> It was always possible to use component callback props, but because you had to listen to dom events using `on:`, it made sense to use `createEventDispatcher` for component events due to > syntactical consiscency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do.
323+
>
324+
> The removal of event modifiers is arguably one of the changes that seems like a step back for those who've liked the shorthand syntax of event modifiers. Given that they are not used that > frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on Dom elements.
325+
>
326+
> Multiple listeners for the same event are also no longer possible, but it was something of an anti-pattern anyway, since it impedes readability: if there are many attributes, it becomes > harder to spot that there are two handlers unless they are right next to each other. It also implies that the two handlers are independent, when in fact something like `event.> stopImmediatePropagation()` inside `one` would prevent `two` from being called.
327+
>
328+
> By deprecating `createEventDispatcher` and the `on:` directive in favour of callback props and normal element properties, we:
329+
>
330+
> - reduce Svelte's learning curve
331+
> - remove boilerplate, particularly around `createEventDispatcher`
332+
> - remove the overhead of creating `CustomEvent` objects for events that may not even have listeners
333+
> - add the ability to spread event handlers
334+
> - add the ability to know which event handlers were provided to a component
335+
> - add the ability to express whether a given event handler is required or optional
336+
> - increase type safety (previously, it was effectively impossible for Svelte to guarantee that a component didn't emit a particular event)
341337
342338
## Snippets instead of slots
343339

@@ -427,16 +423,15 @@ In Svelte 4, you would pass data to a `<slot />` and then retrieve it with `let:
427423
{/if}
428424
```
429425

430-
### Why we did this
431-
432-
Slots were easy to get started with, but the more advanced the use case became, the more involved and confusing the syntax became:
433-
434-
- the `let:` syntax was confusing to many people as it _creates_ a variable whereas all other `:` directives _receive_ a variable
435-
- the scope of a variable declared with `let:` wasn't clear. In the example above, it may look like you can use the `item` slot prop in the `empty` slot, but that's not true
436-
- named slots had to be applied to an element using the `slot` attribute. Sometimes you didn't want to create an element, so we had to add the `<svelte:fragment>` API
437-
- named slots could also be applied to a component, which changed the semantics of where `let:` directives are available (even today us maintainers often don't know which way around it works)
438-
439-
Snippets solve all of these problems by being much more readable and clear. At the same time they're more powerful as they allow you to define sections of UI that you can render _anywhere_, not just passing them as props to a component.
426+
> [!DETAILS] Why we did this
427+
> Slots were easy to get started with, but the more advanced the use case became, the more involved and confusing the syntax became:
428+
>
429+
> - the `let:` syntax was confusing to many people as it _creates_ a variable whereas all other `:` directives _receive_ a variable
430+
> - the scope of a variable declared with `let:` wasn't clear. In the example above, it may look like you can use the `item` slot prop in the `empty` slot, but that's not true
431+
> - named slots had to be applied to an element using the `slot` attribute. Sometimes you didn't want to create an element, so we had to add the `<svelte:fragment>` API
432+
> - named slots could also be applied to a component, which changed the semantics of where `let:` directives are available (even today us maintainers often don't know which way around it > works)
433+
>
434+
> Snippets solve all of these problems by being much more readable and clear. At the same time they're more powerful as they allow you to define sections of UI that you can render _anywhere_, not just passing them as props to a component.
440435
441436
## Migration script
442437

@@ -521,7 +516,7 @@ app.$on('event', callback);---
521516
+++const app = mount(App, { target: document.getElementById("app"), events: { event: callback } });+++
522517
```
523518

524-
> Note that using `events` is discouraged — instead, [use callbacks](https://svelte-5-preview.vercel.app/docs/event-handlers)
519+
> [!NOTE] Note that using `events` is discouraged — instead, [use callbacks](https://svelte-5-preview.vercel.app/docs/event-handlers)
525520
526521
For `$set`, use `$state` instead to create a reactive property object and manipulate it. If you're doing this inside a `.js` or `.ts` file, adjust the ending to include `.svelte`, i.e. `.svelte.js` or `.svelte.ts`.
527522

0 commit comments

Comments
 (0)