Svelte Creator Harris Rejects React Virtual DOM in Compiler Push

Rich Harris - Rethinking reactivity — You Gotta Love Frontend

You Gotta Love Frontend

Runtime virtual DOM diffing imposes unnecessary performance overhead that frameworks can eliminate by shifting reactivity directly into build-time compiler code.

AI-assisted summary of Rich Harris - Rethinking reactivity — original source. Check the original for full context.

Source published:

Key ideas

  • Svelte 3 reframes frameworks as build-time compilers rather than runtime libraries, eliminating the virtual DOM to compile declarative code into surgical DOM mutations instrumented around standard JavaScript assignments. (6:39)
  • By co-opting JavaScript labeled statements ($:), Svelte implements topologically ordered reactive declarations and side effects without the manual dependency arrays or boilerplate required by hooks like useMemo. (13:40)
  • Architectural workarounds like React's Concurrent Mode treat the symptoms of runtime diffing overhead rather than root causes, whereas compile-time reactivity delivers buttery-smooth performance even on constrained platforms like 200,000 Brazilian point-of-sale terminals. (19:35)
  • Operating as a compiler decouples framework capabilities from shipped bundle size, enabling built-in features such as scoped CSS, a11y compiler warnings, and off-main-thread CSS animations without penalizing unneeded code paths. (31:54)

Detailed notes

## Background Published on YouTube by You Gotta Love Frontend on April 22, 2019, this presentation features Rich Harris speaking at YGLF 2019. The session description highlights how modern frameworks incur runtime overhead to track state, proposing an alternative architecture that moves reactivity into the language itself via compilation to make applications smaller and faster.

## TL;DR Rich Harris critiques the runtime costs and abstraction leaks of virtual DOM-based frameworks like React, tracing the history of reactive programming back to early electronic spreadsheets like LANPAR and Lotus 1-2-3. He presents Svelte 3, which fundamentally rethinks reactivity by shifting change detection and DOM updates to a build-time compiler rather than maintaining an in-browser virtual DOM. By instrumenting standard JavaScript assignments and repurposing labeled statements ($:) for topologically sorted derived state, Svelte generates surgical, imperative DOM updates that eliminate reconciliation bottlenecks. Harris demonstrates how this compiled approach dramatically improves real-world performance on constrained devices like point-of-sale terminals and smart TVs, while offering built-in scoped styles, compiler-driven accessibility checks, off-thread CSS transitions, and an approachable syntax that lowers barriers for cross-disciplinary teams.

## Atmosphere & style The presentation feels intellectual, witty, and quietly subversive, striking an engaging balance between historical appreciation and provocative engineering critique. Harris carries an easygoing, self-deprecating stage presence—cracking jokes about Kyle Simpson, playful benchmark trolling, and Candyman—while maintaining unapologetically high engineering standards that question industry orthodoxy. The room moves from chuckles over esoteric JavaScript syntax to genuine awe during live performance demonstrations, creating an inspiring, forward-looking atmosphere centered on craftsmanship, radical simplicity, and broader accessibility.

## Bulletin - [06:39](https://www.youtube.com/watch?v=AdNJ3fydeao&t=399s) Svelte 3 reframes frameworks as build-time compilers rather than runtime libraries, eliminating the virtual DOM to compile declarative code into surgical DOM mutations instrumented around standard JavaScript assignments. - [13:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=820s) By co-opting JavaScript labeled statements ($:), Svelte implements topologically ordered reactive declarations and side effects without the manual dependency arrays or boilerplate required by hooks like useMemo. - [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) Architectural workarounds like React's Concurrent Mode treat the symptoms of runtime diffing overhead rather than root causes, whereas compile-time reactivity delivers buttery-smooth performance even on constrained platforms like 200,000 Brazilian point-of-sale terminals. - [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) Operating as a compiler decouples framework capabilities from shipped bundle size, enabling built-in features such as scoped CSS, a11y compiler warnings, and off-main-thread CSS animations without penalizing unneeded code paths.

## Key takeaways - [00:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=6s) Electronic spreadsheets pioneered reactive programming through forward-referencing dependency graphs, allowing engines to recalculate only changed values rather than recomputing the whole sheet. - [04:08](https://www.youtube.com/watch?v=AdNJ3fydeao&t=248s) React popularized declarative component architecture, but continuously diffing virtual DOM trees and re-executing entire component bodies creates unnecessary runtime overhead for simple state changes. - [05:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=354s) Performance optimization APIs such as shouldComponentUpdate, React.PureComponent, and useMemo serve as abstraction leaks where developers must manually instruct the computer on what not to re-render. - [06:39](https://www.youtube.com/watch?v=AdNJ3fydeao&t=399s) Frameworks should function as tools for organizing thought at build time rather than runtime libraries running inside the client's browser. - [09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) Svelte 3 instruments native JavaScript variable assignments by injecting calls to invalidate, batching surgical DOM mutations at the end of the microtask loop. - [09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) For server-side rendering, Svelte compiles markup into raw string concatenation instead of instantiating component trees in memory, substantially reducing server overhead. - [13:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=820s) Svelte repurposes valid JavaScript labeled statements ($:) to declare derived state and side effects, topologically sorting evaluation order at compile time much like Observable notebooks. - [17:09](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1029s) Drawing a strict philosophical line between runtime reactivity magic (such as Vue's property accessors) and build-time compiler magic is an arbitrary distinction. - [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) Time slicing and concurrent rendering improve responsiveness by breaking virtual DOM tasks into chunks, but they fail to resolve underlying rendering inefficiencies during heavy interactive workloads. - [22:15](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1335s) Reimplementing the virtual DOM in low-level languages like Rust compiled to WebAssembly (as in Dodrio) cannot bypass the architectural bottlenecks inherent to runtime diffing. - [25:00](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1500s) Resource constraints in the modern embedded web—ranging from Smart TVs to payment terminals—make lightweight compile-time approaches essential where traditional JavaScript runtimes falter. - [26:32](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1592s) Because Svelte parses the entire template at build time, it provides built-in accessibility linting and automatically scopes CSS while stripping unused selectors from output bundles. - [30:29](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1829s) Svelte compiles component transitions into native CSS keyframe animations that run off the browser's main thread, avoiding battery-draining JavaScript animation loops. - [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) A compiler-first framework decouples feature breadth from bundle size, allowing rich animations and conveniences to exist in the framework without forcing every project to pay the bundle penalty. - [35:26](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2126s) By mirroring basic HTML, CSS, and plain JavaScript, Svelte lowers the technical barrier to entry so non-engineers like product managers can directly inspect and edit component logic.

## Detailed walkthrough ### [00:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=6s) From LANPAR and VisiCalc to Lotus 1-2-3

Rich Harris opens with what sounds like a grandiose tribute to the moon landing—fifty years ago, humanity entered a new era when two heroic explorers took a small step into a new world, sparking technological breakthroughs that transformed agriculture, medicine, business, and consumer products. But the punchline quickly follows: he is talking about electronic spreadsheets. In 1969, René Pardo and Remy Landau came together to create LANPAR Technologies. Aside from being a play on their names, LANPAR stood for "Language for Programming Arrays at Random," and it pioneered the feature most fundamental to modern spreadsheets: forward referencing, or what is known today as reactive programming.

To demonstrate, Harris shows a spreadsheet tracking his fruit purchasing habits. Columns B and C contain raw prices and quantities, while column D contains formulas whose values derive directly from other cells. Buying an extra cantaloupe causes the subtotal and total to update automatically. The spreadsheet maintains an internal dependency graph where cells connect as inputs and outputs. Adding another formula dynamically alters that graph so that existing cells branch into multiple outputs. In 1969, when computing power was scarce, this dependency graph was essential because calculating only what had changed was vastly more efficient than recalculating the entire sheet from scratch.

```mermaid flowchart TD accTitle: Spreadsheet Dependency Graph accDescr: Data flow from raw cell values to calculated totals in a spreadsheet subgraph Inputs["Inputs"] B["Price (Column B)"] C["Quantity (Column C)"] end subgraph Derived["Formulas"] D["Subtotal (Column D)"] T["Grand Total"] end B --> D C --> D D --> T ```

Yet LANPAR never became a household name; it was simply too far ahead of its time and faded into a computing footnote. When people think of the first electronic spreadsheet, they usually think of VisiCalc. Steve Jobs credited VisiCalc with driving the early commercial success of the Apple II, but VisiCalc took a much more primitive, naive approach without forward referencing. Whenever users edited a cell, the software forced recalculations cell by cell across the board, making it sluggish and clunky. Users tolerated it because they knew no better—until Lotus 1-2-3 arrived with proper forward referencing, unlocking massive performance and utility while relegating VisiCalc to irrelevance almost overnight.

[02:33](https://www.youtube.com/watch?v=AdNJ3fydeao&t=153s) What Is Reactive Programming?

Introducing himself as a graphics editor at *The New York Times*, Harris explains that his talk, "Rethinking Reactivity," is about applying the hard-won lessons of spreadsheet history to modern web software—aiming to build tools that are "a little bit less VisiCalc and a little bit more Lotus 1-2-3." Addressing the question of what reactive programming actually means, he borrows a definition shared the previous day by Michel Weststrate:

> “The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.”

At its core, reactive programming is about data flow: tracking values as they travel through an application so that when an underlying input changes, the rest of the application automatically reacts. Turning to React, Harris acknowledges that it was a genuine miracle when released in 2013, introducing patterns that reshaped the entire web development community. Year after year, the React team has reliably solved problems developers did not even realize they had. Even so, Harris insists the community can do far better. Acknowledging the awkwardness of critiquing popular libraries—especially with a React core contributor, Brendan, sitting directly in the front row—he argues that candid criticism is vital for collective growth, reminding the audience that React's early advocates won over the community precisely by being fiercely critical of the existing frameworks of their own day.

[04:08](https://www.youtube.com/watch?v=AdNJ3fydeao&t=248s) How the Virtual DOM Works and Why React Isn't Reactive

React popularized the virtual DOM, and Harris breaks down step-by-step how a standard React component executes on state updates. Looking at a simple `App` component holding `count` and `name` state alongside click and input handlers, React regenerates a slice of virtual DOM whenever that local state changes. It is then React's responsibility to reconcile the previous tree with the new one:

```mermaid flowchart TD accTitle: React Virtual DOM Reconciliation Walkthrough accDescr: Step-by-step virtual DOM diffing process checking every element A["Check top div (unchanged)"] --> B["Check className 'app' (unchanged)"] B --> C["Check h1 child (unchanged)"] C --> D["Check h1 text content (unchanged)"] D --> E["Check input element & value (unchanged)"] E --> F["Check button element (unchanged)"] F --> G["Check button text ('4' to '5')"] G --> H["Apply DOM Mutation"] ```

- React inspects the top-level `<div>`; it remains a `<div>`, so it is kept. - It checks attributes; the `className` remains `"app"`, so it is kept. - It checks the children; an `<h1>` remains an `<h1>`, so it is kept. - It checks the text node inside the `<h1>`; it hasn't changed, so it is kept. - It checks the `<input>`; it is still an input and its value is identical, so it is kept. - It checks the `<button>`; it is still a button, but its inner text has changed from 4 to 5, so React finally writes that update to the real DOM.

Harris laments the sheer overhead required just to change a single digit: "All that work to change a 4 to a 5. As engineers, we should be offended." Yet the reconciliation walk is not even the most wasteful aspect. The deeper flaw is that the component function must execute repeatedly on every single state change, continuously re-declaring functions that close over local variables. Because React treats the component body as an opaque black box, it lacks any granular understanding of how data flows through the application. As John Lindquist pointed out, “React is a terrible name for React.js.”

[05:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=354s) Is the Virtual DOM Fast?

Harris tackles the persistent myth of virtual DOM performance. Is the virtual DOM fast? "No, it's not fast." Is it fast enough? For data visualization and complex animation, Harris argues categorically that it is not. But developers do not need to take his word for it; the React core team's own architectural additions prove the point. Tools like `shouldComponentUpdate`, `React.PureComponent`, `useMemo`, and `useCallback` exist precisely because the virtual DOM is not inherently fast enough:

> “These are all abstraction leaks that let you, the programmer, tell the computer that it doesn't need to worry about this subtree within your application—in other words, you're doing the computer's job for it.”

When a framework has to invent complex amortization strategies like Concurrent Mode to break up expensive rendering tasks into background slices, it is an unmistakable symptom that runtime diffing costs have become problematic.

[06:39](https://www.youtube.com/watch?v=AdNJ3fydeao&t=399s) Frameworks as Compilers and Svelte 3's Approach

Confronting this performance bottleneck while wanting the declarative convenience of React led Harris to reconsider what a framework actually is. He argues that a framework does not need to live inside the browser at runtime:

> “Frameworks are not tools for organizing your code; they are tools for organizing your mind.”

Embracing the idea that compilers are the new frameworks, Harris created Svelte. Svelte processes declarative components during the build step and compiles them down into surgical, imperative, low-level JavaScript that directly mutates the DOM without an intermediate runtime virtual DOM.

The critical challenge is change detection: how does an application know when something changed? - Traditional class-based React relies on `this.setState()`, which merges state and triggers a full subtree render. - Modern React relies on Hooks, invoking updater functions like `setCount()` paired to state variables. - Early versions of Svelte mirrored classical React with `this.set()`.

Harris explains that both `this.setState` and `this.set` suffer from being verbose and tying the component to `this`. Relying on `this` introduces awkward lexical constraints and unexpected ergonomics. JavaScript already has a native mechanism for signaling change: the assignment operator (`count += 1`). Vue recognized this early on by intercepting property assignments on `this` (`this.count += 1`), but it still required an enclosing component instance at runtime. Because Svelte is a compiler, it is not bound by normal runtime JavaScript constraints. With Svelte 3, reactivity is moved entirely out of API calls and directly into the language syntax itself.

[09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) How Svelte Works: Assignments, Invalidation, and SSR

Demonstrating a Svelte component, Rich Harris replaces React’s `useState` hooks with plain JavaScript assignments: `let name = 'world'` and `let count = 0`. Instead of invoking a setter function, updating the counter simply requires assigning directly to the variable with `count += 1`. Typing into an input field or clicking a button immediately updates the view. To answer how this is possible without a virtual DOM, Harris examines the code emitted by the Svelte compiler. The compiler leaves the original user code almost entirely intact; it simply instruments state changes by injecting calls to an internal `invalidate` function right after any assignment to a variable referenced inside the template markup. The job of `invalidate` is straightforward: it notifies the component that a value has changed and schedules a DOM update to batch at the end of the current microtask/event loop tick.

Harris streamlines the code even further by replacing manual event handlers with Svelte’s two-way `bind:` directive and inlining the button click handler, demonstrating that Svelte achieves the same interactive behavior with a fraction of the boilerplate. Because Svelte places JavaScript inside HTML rather than embedding HTML inside JavaScript via JSX, the compiler can target vastly different outputs based on operational needs. In the browser, it outputs a lightweight JavaScript class that manipulates the DOM directly. For server-side rendering (SSR), it compiles the template into plain string concatenation. Creating a serialized component tree in memory is unnecessary when the server can simply stitch strings together, making Svelte’s SSR mode orders of magnitude faster. Harris quips: “What this means is that if you do server-side rendering with Svelte, your AWS bills will be lower—tell your product managers.”

```mermaid flowchart TD accTitle: Svelte Update Lifecycle accDescr: Diagram showing how Svelte instruments assignments with invalidate to update the DOM at the end of the event loop.

subgraph CompileTime["Compile Time"] A["User Code: count += 1"] --> B["Svelte Compiler"] B --> C["Instrumented Output: invalidate('count', count += 1)"] end

subgraph Runtime["Runtime Event Loop"] C --> D["invalidate() records dirty state"] D --> E["Batch updates at end of microtask"] E --> F["Direct DOM Mutation"] end ```

- Consuming a Svelte component in a build pipeline is identical to importing any standard JavaScript module; a bundler plugin converts the `.svelte` file on the fly into an ES class. - Client-side initialization uses standard instantiation: `new App({ target: document.body })`. - Server-side execution exposes an optimized string-rendering API: `App.render()`.

---

[11:42](https://www.youtube.com/watch?v=AdNJ3fydeao&t=702s) Derived State: React's useMemo vs. Svelte's Dilemma

State in non-trivial applications rarely exists in isolation; components frequently contain derived values that depend directly on other pieces of state, much like formula cells in a spreadsheet. In React, this is handled naturally because the entire component function re-executes on every render. If a component has an array of todos and a boolean toggle to hide completed items, a derived variable like `const filtered = todos.filter(...)` recalculates automatically whenever the component renders, and the JSX maps over the resulting array. However, Harris points out that this default execution model has a serious flaw: if the parent component re-renders, the child recalculates its derived values unconditionally, even when none of their underlying dependencies have changed.

To solve this in React, developers reach for `useMemo`, wrapping the computation in a callback and providing an explicit dependency array such as `[todos, hideDone]`. React inspects the dependency values from the previous render, and if nothing changed, skips the calculation and returns the cached result. While this avoids wasted work, Harris stresses that it offloads mechanical bookkeeping onto developers: “That’s a bit more efficient, but it’s obviously more work for the developer—it’s more boilerplate for us.” Comparing the full React component with its Svelte equivalent, Svelte’s syntax is visibly leaner; Harris notes that across many components he has converted, React equivalents consistently demand around 40% more code. Yet, directly translating that component into Svelte reveals a fundamental problem: when clicking todos to toggle them, the UI refuses to update. Because Svelte runs its `<script>` block only once upon initialization, `const filtered` stays frozen forever, forcing the framework to find a new way to express reactive dependencies.

---

[13:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=820s) Topological Ordering and the Labeled Statement ($:)

To solve derived reactivity without re-running entire components, Harris looks outside standard UI frameworks to Mike Bostock, the creator of D3 and Observable. Observable notebooks differ radically from traditional top-to-bottom runtimes like Jupyter or RunKit by executing cells in topological order. If statement A depends on statement B, statement B is guaranteed to evaluate first regardless of where it appears in the source file, operating exactly like an electronic spreadsheet. Harris also cites an essay titled *What is reactive programming?* by Australian developer Paul Stovell, which imagines code anthropologists in the year 2051 looking back at modern JavaScript and wondering why updating `a = 10` failed to automatically update `b = a + 1`. In Stovell’s speculative future, developers use a dedicated “Destiny operator” (`b <= a + 1`) to permanently bind relationships into the language itself.

Inventing a new operator in JavaScript is impossible because non-standard syntax breaks parsers, linters, and editor tooling. Instead, the Svelte team co-opted an obscure, valid feature of JavaScript syntax that almost no one uses: labeled statements. By prefixing declarations with `$:` (such as `$: filtered =...`), Svelte binds the variable reactively. Harris jokes about repurposing this syntax: “Does anyone recognize this? This is a thing called a labeled statement, and it doesn't really do anything most of the time in JavaScript, so we on the Svelte team have co-opted it—we've made it our own.” He playfully adds that Svelte has become “the guy Kyle Simpson was trying to warn you about.” Reactive declarations can also be chained (`$: showing = filtered.length`), and Svelte automatically topologically sorts them at compile time, guaranteeing that `filtered` recalculates before `showing`. Furthermore, `$:` is not limited to variable assignments; developers can attach arbitrary statements or side effects, such as `$: console.log(filtered)`, creating an effortless tool for debugging state changes over time.

- In Observable notebooks and Svelte reactive declarations, execution order is determined strictly by dependency graphs rather than lexical line position. - Svelte compiles `$:` statements into compact conditional checks: if either `todos` or `hideDone` changes, the compiled component recalculates `filtered` and issues an `invalidate('filtered')` signal before calculating downstream dependents.

---

[17:09](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1029s) The "SvelteScript" Debate and Magic at Compile Time

Svelte’s reliance on labeled statements has sparked debate across the front-end community. Harris notes that critics often dismiss the syntax as the "bad kind of magic." He highlights a reaction from Evan You, the creator of Vue: “Evan You, the creator of Vue.js, said, 'Well, this isn't even JavaScript, this is technically SvelteScript.' And he's right—we are abusing the language for fun and profit.” However, Harris finds it ironic that Evan of all people would voice this critique, given that Vue popularized making JavaScript property assignments carry reactive side effects via runtime getters and setters. You’s critique hinges on distinguishing runtime browser magic from build-time compiler magic, a division Harris rejects as entirely arbitrary: “I think if you draw a line between those two things, then you're guilty of making the same mistake as people who used to say that templates and styles and JavaScript should be in separate files even though they relate to the same component.”

Harris shifts to performance comparisons, admitting to a mischievous habit: “Something I like to do when framework authors release benchmarks showing how fast their framework is, I add Svelte to it and then I tweet out the result—it is probably why I don't have many friends, but it's so worth it.” He displays a benchmark demonstrating Svelte running 35 times faster than React and 50 times faster than Vue. Yet, Harris readily concedes a point raised by Dan Abramov of the React team: synthetic benchmarks that pump hundreds of thousands of updates per second into a component are meaningless when human eyes consume screens refreshing at 60 Hertz. The true priority is perceived user experience, which leads directly to the core rationale behind React’s Concurrent Mode. Referencing Abramov’s influential JSConf Iceland talk, Harris opens a demo running an artificially throttled CPU where typing into an input field stutters visibly under standard synchronous rendering, setting the stage for how frameworks should tackle real-world responsiveness.

[19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) The Limits of Time Slicing vs. Svelte's Raw Speed

Rich Harris demonstrates how typical applications handle heavy loads on the main thread, contrasting common optimization patterns with Svelte's compiled architecture. When firing up a live "frustration meter" alongside an interactive data-heavy graph, unoptimized updates quickly choke the browser. Historically, developers resort to debouncing—waiting until the user stops typing to trigger an update. While debouncing unblocks the main thread and keeps the input field responsive, Harris notes it yields a poor user experience because the user receives zero real-time visual feedback while interacting.

React's concurrent mode (originally introduced as async mode or time slicing) attempts to offer the best of both worlds: the graph updates live, and if the workload becomes too intense, it slices the rendering tasks into smaller chunks and yields execution back to the main thread. This prevents total input freeze and keeps the frustration meter green. However, Harris reveals the catch: as the workload's complexity grows, the background graph lags visibly and becomes "chunky." He then switches over to the exact same application implemented in Svelte, where both the input and the underlying graphic update instantly and buttery-smooth without any perceptible delay or frame drops.

Addressing an audience suggestion about Inferno, Harris acknowledges it would likely outperform React, but emphasizes that Inferno still relies on a Virtual DOM mechanism and will ultimately encounter the same foundational bottlenecks. While time slicing is undeniably superior to vanilla React without it, Harris argues it treats the symptoms rather than the disease.

> “The best way to deliver a good user experience is to be extremely fast.” — Rich Harris

```mermaid flowchart TD accTitle: Strategies for Handling Heavy UI Updates accDescr: Diagram comparing Debouncing, Concurrent Mode, and Svelte compiled DOM updates. subgraph S1["Debouncing"] D1["User Input"] --> D2["Delay Execution"] D2 --> D3["Batch Run on Pause"] D3 --> D4["Zero Feedback While Typing"] end

subgraph S2["Concurrent / Async Mode"] C1["User Input"] --> C2["Chunk Work in Virtual DOM"] C2 --> C3["Yield to Main Thread"] C3 --> C4["Input Responsive, but Graph Lags"] end

subgraph S3["Svelte Direct Updates"] V1["User Input"] --> V2["Surgical DOM Mutations"] V2 --> V3["No Virtual DOM Overhead"] V3 --> V4["Instant Real-Time UI Updates"] end ```

[22:15](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1335s) The Internal Combustion Analogy and Dodrio's Rust/Wasm Benchmark

Harris draws an analogy to automotive engineering: the modern internal combustion engine (ICE) is an astonishing marvel of mechanical engineering, honed over decades through millions of dollars in research to squeeze out fractional improvements. Yet an ICE contains hundreds of moving parts and tops out at an energy efficiency of roughly 20% to 25%. When Tesla entered the market, they threw out the premise of fossil fuels entirely, using an electric motor with only two moving parts and an efficiency of 85%. In the web world, trying to optimize Virtual DOM diffing by slicing tasks across threads or cycles mirrors car companies pouring fortunes into squeezing marginal gains out of fundamentally inefficient engines.

Harris notes that moving code around never genuinely accelerates it. Slicing work into chunks merely changes perceived responsiveness, and past fads like offloading UI logic into Web Workers failed because shuttling data across thread boundaries is too costly. Even rewriting the Virtual DOM in low-level systems languages like Rust compiled to WebAssembly cannot escape structural architectural flaws. Citing a blog post by Nick Fitzgerald about Dodrio—a Rust-based Virtual DOM library compiling to Wasm that claimed "best-in-class performance"—Harris jokes about his competitive nature:

> “Now when you say best-in-class performance, it's like if you stand in front of the mirror and say Candyman five times, I will appear behind you.” — Rich Harris

Harris benchmarked Svelte against Dodrio. Upon discovering that Dodrio's original benchmark used `setTimeout`—meaning most runtime was spent idling on the browser's timer—Harris refactored it to use microtask promise resolution to measure true DOM mutation speeds. Svelte outperformed Dodrio by a substantial margin. While Harris remains enthusiastic about Rust and Wasm for heavy computational jobs, he maintains that applying a Virtual DOM architecture will never produce ultimate rendering speed. Borrowing a philosophy from Marie Kondo, he urges developers to speed up code by asking if their JavaScript "sparks joy," and throwing the unnecessary runtime away.

[25:00](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1500s) Real-World Performance: Point-of-Sale Terminals and the Embedded Web

Harris pushes back hard against critics who claim that obsessive performance optimization is merely an ivory-tower hobby detached from genuine engineering needs:

> “When people like me talk about performance, we are often accused of having our heads in the clouds, of not being concerned with the real-world impacts of our applications, and I find that so confusing: performance is the most real-world thing there is.” — Rich Harris

To demonstrate real-world impact, Harris highlights Stone, a financial services company in Brazil producing point-of-sale (POS) payment terminals. Stone initially attempted to build the device software using React, Vue, and a range of competing libraries. The devices were simply too underpowered, resulting in sluggish, unusable software. Stone rewrote the interface in Svelte, and today over 200,000 POS terminals run Svelte directly on the streets of Brazil.

A second case study is Moslab in Russia, which engineers software for low-resource environments like Smart TVs. Svelte offered them rapid developer productivity without demanding performance compromises. Moslab has since expanded Svelte's footprint into internal products such as smart-home automation dashboards. Harris underscores that the mobile web is no longer the new frontier; modern frontend engineering is expanding into embedded devices, Internet of Things (IoT), wearables, in-car dashboard infotainment systems, and Smart TVs. In these constrained environments, resource-heavy JavaScript-first runtimes inevitably fail.

- **Stone (Brazil):** 200,000 payment terminals powered by Svelte across Brazil after React and Vue proved too resource-intensive. - **Moslab (Russia):** Deploys Svelte onto Smart TVs and home automation control panels to overcome limited hardware profiles. - **The Embedded Web:** Wearables, IoT hardware, and automotive infotainment define the modern performance constraints where runtime-heavy frameworks cannot compete.

[26:32](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1592s) Built-in Accessibility Warnings and Scoped CSS

Harris turns his attention to architectural advantages beyond pure rendering speed, beginning with accessibility (a11y). Developers typically intend to build accessible software, but fall short because engineers without visual or motor impairments often overlook non-visual user experiences. Because Svelte functions as a compiler that understands the entire template, it can catch access issues before code ships. In a live code demonstration, Harris shows an `<img>` tag provided with an `src` attribute but no `alt` tag. Svelte immediately displays a compiler warning in the terminal and editor: `accessibility: <img> element should have an alt attribute`. Once the alt attribute is added, the warning vanishes.

> “It will let you write markup that isn't accessible, but it won't respect you for it.” — Rich Harris

Harris next tackles CSS. Asking the audience if anyone builds web applications without styles, he notes that most mainstream frameworks neglect styling entirely, leaving developers to piece together third-party CSS-in-JS libraries. Svelte treats components as supersets of HTML, allowing `<style>` blocks to live directly inside component files. Styles are automatically scoped at build time: a bare `p {... }` selector styles the component's paragraphs without leaking into child components or parent wrappers. Svelte generates a unique CSS class hash applied simultaneously to the compiled markup and the generated stylesheet. Furthermore, if a CSS selector in the `<style>` block matches no markup in the component, Svelte issues an unused CSS selector warning and automatically purges the dead styles from the production output.

[29:46](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1786s) Declarative Built-in Transitions

Harris demonstrates that animations and component transitions should not require sprawling external animation engines. By simply importing transition primitives from `svelte/transition` (such as `fly`), developers can attach declarative directives directly to elements in their template:

- Elements smoothly animate into and out of view when bound to state changes, rather than snapping abruptly into and out of the DOM. - Directives like `transition:fly="{{ y: 200 }}"` configure direction and distance via basic JavaScript object literals. - Svelte gracefully handles interruption: toggling the visibility variable mid-flight causes the animation to reverse seamlessly without jumping or resetting layout positions.

[30:29](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1829s) CSS-Driven Animations and Custom Transitions

Rich Harris explains that Svelte manages transitions by smoothly reversing them and restoring elements to their correct places—a task that is notoriously difficult to pull off cleanly in traditional JavaScript frameworks. Crucially, Svelte does not execute these animations through ongoing JavaScript runtime loops. Instead, when the component's state changes, Svelte computes a CSS animation and applies it directly to the target DOM element. Because modern browsers execute CSS animations completely off the main thread, this implementation strategy delivers significantly better runtime performance and is far more battery-friendly, particularly for mobile devices.

```mermaid flowchart TD accTitle: Svelte CSS Transition Pipeline accDescr: Flow showing how Svelte translates state changes into CSS animations that run off the main thread. subgraph SvelteJS["JavaScript Thread"] A["State Change"] --> B["Svelte calculates CSS animation"] B --> C["Apply keyframes/rules to DOM element"] end subgraph BrowserEngine["Off Main Thread"] C --> D["Browser Compositor executes animation"] D --> E["Smooth 60fps rendering without battery drain"] end ```

Harris demonstrates how flexible the system is by showing how developers can construct custom transitions. He walks through a custom `spin` transition function that accepts a DOM `node` alongside optional configuration arguments. The function returns an object containing a `css` method, which receives a normalized time parameter `t` ranging from `0` (completely off the screen) to `1` (completely on the screen) and returns a CSS string. Svelte compiles this dynamically generated CSS into native keyframe animations on the element:

```javascript // Conceptual structure of a Svelte transition function function spin(node, { duration = 400 }) { return { css: (t) => ` transform: scale(${t}) rotate(${t * 360}deg); opacity: ${t}; ` }; } ```

Harris shows how clean the template usage is by importing `spin` from `./spin.js` and applying directives directly to markup: `in:spin out:fade`. This architecture opens the door to non-standard easing equations and intricate visual effects while keeping the underlying execution fully offloaded to CSS.

- “Because CSS animations run off the main thread, this is more performant and more battery friendly particularly on mobile devices than if you're doing the animation inside JavaScript.” — Rich Harris - Custom transition functions take `(node, options)` and return an object with a `css(t)` method where `t` is a value from `0` to `1`. - Svelte can animate using complex easing curves that are not natively supported in vanilla CSS, while still packaging the final result as pure CSS.

---

[31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) Coordinated Lists and Decoupling Features from Bundle Size

To illustrate higher-level UI choreography, Harris presents a to-do list separated into two views: items that are still pending, and completed items below them. Without transitions, toggling a checkbox causes an item to abruptly vanish from one list and instantly appear in the other. Harris highlights that this jarring visual jump contradicts how objects behave in physical space, leaving users disoriented:

> “Our brains don't like it when things just disappear and reappear elsewhere on the screen. What we want is for these things to move gracefully to their new position; that helps us understand what's actually happening to the things inside the application.”

By adding Svelte crossfade transition directives to the list items, the completed tasks smoothly slide across the screen and settle into their new positions, all executed via hardware-accelerated CSS.

Most front-end libraries avoid baking in advanced animation engines because the JavaScript community is hyper-focused on bundle size. Framework landing pages routinely boast metrics like being a "3-kilobyte alternative to React." While shipping fewer bytes over the wire is undeniably good for end users, runtime libraries pay a steep penalty: adding built-in features directly increases the baseline bundle weight for every application.

Because Svelte operates strictly as a compiler, it breaks this trade-off completely. Unused framework capabilities—whether complex transition math, specialized SVG helpers, or two-way bindings—are simply omitted from the compiled output.

- “It means that we've decoupled feature set from file size and that lets us, the Svelte team, be far more expansive in the kinds of features that we consider worth including in the framework.” — Rich Harris - “Developers, as developers, we want all the features, but we just don't want to pay for the ones that we're not using, and what's good for developers is ultimately good for users.” — Rich Harris - Svelte applications bundle only the minimal operational code required to support the exact feature set authored in the source files.

---

[34:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2046s) The Broader Ecosystem: Sapper, Svelte Native, and Svelte GL

Harris explains that while core Svelte operates as a low-level component framework capable of powering complex applications, building comprehensive web products requires high-level application machinery. To meet this need, the team maintains **Sapper**, an application framework companion to Svelte modeled on tools like Next.js and Gatsby for React. Sapper packages essential production features—file-system routing, automatic code splitting, and server-side rendering (SSR)—while producing dramatically smaller JavaScript bundles due to Svelte's compile-time design.

Harris also highlights other projects expanding across the wider ecosystem: - **Svelte Native:** A community-driven project built on top of NativeScript-Vue that allows developers to write mobile applications for iOS and Android using Svelte components. Harris notes it will likely become an official project in the future. - **Svelte GL:** An initiative Harris is eager to build, designed as a Svelte-centric counterpart to Three.js. Developers would declare a 3D scene graph using familiar Svelte component syntax, which the compiler would then transform directly into low-level WebGL draw calls without the abstraction overhead of traditional WebGL engines.

---

[35:26](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2126s) Accessibility, Shared Ownership, and the Power of Spreadsheets

Returning to the broader theme of the presentation, Harris observes that spreadsheets remain one of the most successful reactive computing paradigms ever created because of their radical accessibility. He shares a personal story about his wife, who is not a programmer and has zero desire to become one after living with him for 15 years, yet routinely creates extraordinary value inside businesses using spreadsheets. Harris argues that web development tools should strive to match that level of intuitive approachability:

> “Wouldn't it be wonderful if the tools that we used to build the web were as accessible as spreadsheets are? And that is one of Svelte's overriding goals: is to make web development accessible without sacrificing power for power users.”

To show this accessibility in practice, Harris shares an anecdote from his friend Eliza at Spotify. While building a project with Svelte, her product manager was able to walk over, examine the code, and directly edit component markup and logic on the spot. Because Svelte components look and act like plain HTML, CSS, and basic JavaScript rather than dense, heavily abstracted framework code, they remove the intimidation barrier that usually keeps non-engineers from contributing. Harris concludes that when diverse people with varied disciplines and skill sets can crowd around a single laptop and work together on the same codebase, true breakthroughs occur, expressing his hope that Svelte will ultimately bring teams closer together.

## Notable quotes > [06:39](https://www.youtube.com/watch?v=AdNJ3fydeao&t=399s) Frameworks are not tools for organizing your code; they are tools for organizing your mind. — Rich Harris

> [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) The best way to deliver a good user experience is to be extremely fast. — Rich Harris

> [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) Developers, as developers, we want all the features, but we just don't want to pay for the ones that we're not using, and what's good for developers is ultimately good for users. — Rich Harris

## Mentioned Apple II, Brendan, Dan Abramov, Dodrio, D3, Eliza, Evan You, Gatsby, Inferno, John Lindquist, Kyle Simpson, LANPAR, Lotus 1-2-3, Marie Kondo, Michel Weststrate, Mike Bostock, Moslab, NativeScript-Vue, Next.js, Nick Fitzgerald, Observable, Paul Stovell, React, Remy Landau, René Pardo, Rich Harris, Rust, Sapper, Spotify, Steve Jobs, Stone, Svelte, Svelte GL, Svelte Native, Tesla, The New York Times, Three.js, VisiCalc, Vue.js, WebAssembly

## Video description Modern JavaScript frameworks are all about reactivity. Change your application's state, and the view updates automatically. But there's a catch — tracking state changes at runtime adds overhead that eats into your bundle size and performance budgets. In this talk, we'll discover an alternative approach: moving reactivity into the language itself. Your apps have never been smaller or faster than they're about to become.

Slides: https://rethinking-reactivity.surge.sh/

YGLF 2019: Code Camp Blog - Rich Harris: https://www.israel.yglfconf.com/post/meet-the-speaker-rich-harris

[Source](https://www.youtube.com/watch?v=AdNJ3fydeao)

中文简报

Svelte创始人Harris力推编译器模式,摒弃React虚拟DOM 运行时虚拟DOM比对会带来不必要的性能开销,而框架可以通过将响应性直接转移到构建期编译代码中来消除这一负担。 Rich Harris对React等基于虚拟DOM的框架所产生的运行时开销和抽象泄漏提出了批评,并将响应式编程的历史追溯到LANPAR和Lotus 1-2-3等早期电子表格。他推介了Svelte 3,该版本从根本上重新审视了响应性,通过将变更检测和DOM更新转移到构建期编译器,而不是在浏览器中维护虚拟DOM。通过插桩标准JavaScript赋值操作,并复用标签语句($:)来实现拓扑排序的派生状态,Svelte能够生成精准的外科手术式命令性DOM更新,从而消除协调对比的性能瓶颈。Harris展示了这种编译方案如何显著提升POS终端和智能电视等受限设备上的实际性能,同时提供内置的作用域样式、编译器驱动的无障碍访问检查、脱离主线程的CSS过渡效果,以及降低了跨学科团队协作门槛的易用语法。 Svelte 3将框架重新定义为构建期编译器而非运行时库,摒弃了虚拟DOM,将声明式代码编译为围绕标准JavaScript赋值进行插桩的精准DOM变更。 (6:39) 通过借用JavaScript标签语句($:),Svelte实现了按拓扑排序的响应式声明和副作用,无需像useMemo等Hooks那样使用手动依赖项数组或样板代码。 (13:40) 像React并发模式(Concurrent Mode)这类架构上的融通方案,只是在治标而非根除运行时比对开销的病因;相比之下,编译期响应性即使在巴西20万台POS终端等受限平台上,也能提供丝滑流畅的性能表现。 (19:35) 以编译器形式运行使得框架功能与发布的打包体积解耦,从而在不增加无用代码路径负担的情况下,实现作用域CSS、无障碍访问(a11y)编译警告以及脱离主线程的CSS动画等内置特性。 (31:54)

Reading summary

Rich Harris argues that modern web frameworks should abandon runtime Virtual DOM diffing in favor of build-time compilers that generate surgical DOM mutations, drawing inspiration from the reactive dependency models pioneered by electronic spreadsheets. Reactivity Lessons from Spreadsheets and React's Limits (0:06) Harris traces the origins of reactive programming back to the 1969 spreadsheet LANPAR, which introduced forward referencing to recalculate only changed cells via a dependency graph. While VisiCalc succeeded commercially despite recalculating every cell on edit, Lotus 1-2-3 soon displaced it by adopting forward referencing. Harris contrasts this model with React, which he argues is not genuinely reactive: updating a component executes the entire component function and reconciles the resulting Virtual DOM tree node by node. Harris considers this reconciliation wasteful, pointing out that tools like useMemo, useCallback, and Concurrent Mode exist as abstraction leaks to compensate for inherent Virtual DOM overhead. Compilers as Frameworks: Svelte's Execution Model (6:39) Rather than relying on runtime state APIs like this.setState or hooks, Svelte treats the framework as a compiler. It instruments standard JavaScript variable assignments (such as count += 1) by injecting an internal invalidate call that schedules targeted DOM mutations at the end of the microtask queue. To manage derived state without re-running entire components, Svelte adapts JavaScript's native labeled statements ($:) to declare dependencies, which the compiler sorts into a topological execution order modeled after Observable notebooks. Because components are compiled rather than interpreted through a runtime virtual tree, server-side rendering can compile templates directly into plain string concatenation. Real-World Performance Beyond Synthetic Benchmarks (19:35) Harris contends that performance optimization is critical for constrained computing environments rather than an academic exercise. While React's Concurrent Mode splits Virtual DOM rendering across ticks to preserve input responsiveness, Harris demonstrates that background visualizations can still lag under heavy loads, whereas direct DOM mutation maintains smooth updates. To illustrate real-world relevance, Harris highlights Stone, which deployed Svelte across 200,000 point-of-sale terminals in Brazil after React and Vue proved too resource-intensive, and Moslab, which uses Svelte on resource-constrained Smart TVs and home automation hardware. Decoupling Built-in Features from Bundle Size (26:32) Runtime frameworks frequently exclude built-in features to avoid ballooning baseline JavaScript bundle sizes. In contrast, Svelte compiler architecture strips unreferenced framework code at build time, allowing the platform to bundle native accessibility warnings, scoped CSS with automated dead-code elimination, and declarative UI transitions without penalizing unused components. Furthermore, Svelte implements animations by generating CSS keyframes off the main thread rather than running JavaScript loops, reducing CPU and battery consumption on mobile hardware. Harris concludes that the ultimate purpose of aligning web tools with spreadsheet-style reactivity is accessibility. By sticking close to plain HTML, CSS, and basic JavaScript instead of complex runtime abstractions, frameworks can lower collaboration barriers so non-engineers and product managers can directly understand and edit production code.

精炼阅读摘要

Rich Harris认为现代前端开发应当借鉴电子表格的细粒度响应式模型,指出虚拟DOM并非性能优化的终点,而Svelte通过编译器架构将响应式与更新逻辑移至构建期,从而在大幅削减运行时开销的同时提升开发与交互体验。 虚拟DOM的开销与响应式误区 (0:06) Harris借用电子表格演进史指出,1969年LANPAR通过前向引用与依赖图实现了仅计算变动单元格的高效更新,而初期缺乏依赖图的VisiCalc则因全局重算而迟缓,直到Lotus 1-2-3采用正确机制才彻底改变格局。他认为现代前端在响应式处理上仍类似于VisiCalc。以2013年推出的React为例,虽然其模式重塑了行业,但组件在状态更新时必须完整重新执行函数体并生成新的虚拟DOM树,随后逐层对比节点、属性和文本。这种机制仅为修改单个数值就需遍历大量无变化节点,且由于将组件视作不透明黑盒,缺乏对数据流向的细粒度感知。 Harris明确反驳了“虚拟DOM速度快”的说法,认为在复杂动画与数据可视化等场景下其性能难以满足需求。诸如shouldComponentUpdate、PureComponent、useMemo与useCallback等API的诞生,本质上属于抽象泄漏,是迫使开发者手动承担计算机本应完成的子树优化。React提出的并发模式(Concurrent Mode)或时间切片(Time Slicing)虽然能将任务拆分以避免输入卡死,但 Harris认为这只是在缓解表面症状,复杂负载下背景图表仍会出现肉眼可见的卡顿,并未根除运行时diffing的核心损耗。 编译器架构与语言原生响应式 (6:39) Harris主张前端框架不应单纯作为浏览器运行时的组织工具,而应扮演编译器的角色。Svelte在构建阶段解析声明式组件,直接将其转换为低级且精准修改真实DOM的原生JavaScript指令,从而省去中间虚拟DOM层。在变更检测层面,传统React类组件依赖this.setState,函数组件借助Hook的更新函数,这些做法不仅增加样板代码,还受制于运行时的上下文约束。Svelte则直接将普通赋值操作(如count += 1)作为触发变更的原生信号。 在底层实现上,Svelte编译器在检测到模板所引用的变量发生赋值时,会自动注入内部的invalidate调用,在微任务或事件循环末尾批量调度真实DOM修改。对于派生状态,Svelte借鉴Observable等响应式笔记本的拓扑排序理念,借用了JavaScript合法但冷门的标签语句语法($:)。开发者使用$:声明的计算属性和副作用会在编译期分析依赖图并实现拓扑排序,确保上游变量更新后按正确顺序重算。尽管Vue作者Evan You等批评者质疑此举发明了“SvelteScript”并滥用语法,但Harris认为将编译期转换与运行时拦截(如Vue的getter/setter)人为对立毫无必要。 硬件受限场景下的真实性能验证 (19:35) 针对性能优化脱离实际工程需求的质疑,Harris指出性能是影响业务落地的关键现实指标。他将持续优化虚拟DOM比作车企耗费巨资改良仅有20%至25%热效率的内燃机,而放弃运行时diffing则类似于转向效率达85%的纯电驱动。即便利统语言(如Rust编写并编译为Wasm的Dodrio)重写虚拟DOM,在修正基准测试消除setTimeout等待后,其实际DOM变动性能依然落后于Svelte。 在生产环境中,低算力嵌入式硬件进一步放大了框架运行时的性能瓶颈。巴西金融服务企业Stone在为POS刷卡机开发软件时,曾尝试React和Vue但均因设备算力不足而导致界面严重卡顿,最终采用Svelte重写,并在全巴西部署了超过20万台终端。俄罗斯Moslab同样在智能电视和智能家居中控屏等资源受限环境中采用Svelte构建界面。Harris强调,伴随物联网设备、穿戴设备与车载信息娱乐系统的普及,沉重的JavaScript运行时已难以应对现代受限硬件的严苛挑战。 内置工程特性与包体积解耦 (26:32) 除了运行速度,Harris展示了编译器对整体开发流程的架构赋能。由于Svelte编译器能完整解析模板语义,可在编译期实施无障碍(a11y)检查,例如img标签缺失alt属性时会在终端和编辑器中直接告警。在样式方面,Svelte支持组件内编写style标签,编译期自动生成类哈希实现CSS作用域隔离,并能检测且剔除模板中未使用的冗余选择器。 在动效实现上,Svelte内置声明式转场指令(如transition:fly),由框架在状态变动时动态计算并挂载原生CSS关键帧动画。由于动画交由浏览器合成线程在主线程之外独立执行,不仅保障了60fps的流畅表现,还降低了移动设备的电池消耗,且中途打断状态时能平滑反转。传统运行时框架为限制包体积(如标榜自身仅3KB),往往不敢内建复杂的动效或SVG工具;Svelte因采用编译器机制,未使用的功能在打包时会被彻底剥离,成功实现了框架功能集与产物体积的解耦。 应用级生态扩展与协作平民化 (34:06) 在单组件开发之外,Harris介绍了Svelte向完整应用工程延伸的生态版图:官方维护的Sapper对标React生态的Next.js与Gatsby,提供基于文件系统的路由、代码分割及服务端渲染(SSR)。得益于编译机制,Svelte在SSR模式下直接编译为原生字符串拼接,相比内存序列化组件树大幅减轻服务端压力。此外,社区还拓展了面向移动跨平台的Svelte Native,以及探索利用声明式语法直接编译为底层WebGL调用的Svelte GL。 Harris回顾指出,电子表格之所以具有强大的生命力,在于其极高的可访问性,能让非程序员轻松建立复杂计算模型。Svelte力图在不牺牲高级开发能力的前提下消除前端抽象门槛,其组件由标准HTML、CSS和基础JavaScript构成,避免了过度包装。他援引Spotify团队的实践提到,产品经理能够直接查看并修改Svelte组件的标记与业务逻辑,证明平易近人的设计能打破专业壁垒,促成跨职能团队围绕同一代码库开展无障碍协作。 Harris的论述核心在于倡导前端框架从“运行时逐层比较”转向“编译期生成精准更新”,并通过生态扩展降低软硬件与人际协作的门槛。但演讲展示的高性能场景多集中在极端CPU节流测试与受限嵌入式硬件,对复杂常规Web应用中宏观状态管理及生态成熟度的潜在挑战尚未做进一步展开。

Detailed notes — zh

## Background 本内容由频道 You Gotta Love Frontend 于 2019 年 4 月 22 日发布在 YouTube 上,记录了 Svelte 创造者 Rich Harris 在 YGLF 2019 大会上的演讲。页面简介指出,现代 JavaScript 框架普遍依赖响应式机制,但运行时追踪状态变更的开销严重侵蚀了打包体积与性能预算,而本场演讲将揭示一种将响应式能力移至编程语言本身的全新途径,让应用变得前所未有的轻巧与快速。 ## TL;DR Rich Harris 从 1969 年早期电子表格 LANPAR 和 Lotus 1-2-3 的“前向引用”历史切入,尖锐批判了 React 虚拟 DOM 协调比对带来的庞大运行时开销及 `useMemo` 等抽象泄漏。他提出“框架即编译器”的核心理念,详细演示了 Svelte 3 如何通过编译期插桩、JavaScript 原生赋值操作符和标号语法(`$:`)实现细粒度的拓扑响应式更新。此外,演讲通过高负载渲染基准测试、嵌入式 Web(如巴西 20 万台 POS 刷卡机)落地案例,展现了脱离虚拟 DOM 的极致性能,并介绍了 Svelte 内置的 a11y 检查、作用域样式、脱离主线程的 CSS 动画以及 Sapper 等全栈生态。最终,他呼吁让 Web 开发工具像电子表格一样直观普惠,打破工程师与非技术人员之间的协作壁垒。 ## Atmosphere & style 整场演讲充满幽默风趣却又极其锐利的批判锋芒,Rich Harris 展现了极具煽动性且从容自信的舞台把控力。开场他借登月登峰造极的宏大叙事引出电子表格,引得全场会心大笑;在直面台下 React 核心开发者时,他诚恳致歉却又毫不留情地用“作为工程师我们应当感到被冒犯”击碎虚拟 DOM 的神话。现场编码与极端负载 Demo 的流畅对比令人信服,而在尾声处,内燃机向电动机的隐喻以及非技术同事围坐修改代码的温情故事,将一场深度的硬核编译器技术分享升华为对技术普惠与人文连接的动人愿景。 ## Bulletin - [02:51](https://www.youtube.com/watch?v=AdNJ3fydeao&t=171s) Rich Harris 指出虚拟 DOM 并非真正的响应式且存在性能天花板,React 为此引入的 useMemo 和 Concurrent Mode 等机制本质上是要求开发者替运行时充当优化器的抽象泄漏。 - [06:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=400s) Svelte 3 采用“框架即编译器”的范式,抛弃了 this.setState 和 Hooks 等复杂 API,直接将 JS 原生赋值(如 count += 1)及标签语法($:)在编译期转化为细粒度精准操作 DOM 的命令式代码。 - [25:00](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1500s) 在嵌入式 Web 和低算力硬件场景下,传统运行时框架面临严重瓶颈,而 Svelte 已在巴西 20 多万台刷卡 POS 机和智能电视等资源受限设备上成功规模化落地。 - [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) 得益于编译器架构,Svelte 将特性丰富度与包体积彻底解耦,内置了离线 Compositor 线程 CSS 动画、无障碍编译告警及样式死代码剔除,未使用的功能绝不会打包下发给用户。 ## Key takeaways - [00:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=6s) 现代响应式编程的核心起源于 1969 年 LANPAR 电子表格引入的“前向引用”与依赖图模型,旨在按需刷新受影响单元格而非盲目全量计算。 - [02:51](https://www.youtube.com/watch?v=AdNJ3fydeao&t=171s) React 将组件内部视为黑盒,每次状态变化都会全量递归遍历比对虚拟 DOM 树并反复创建闭包函数,严格意义上并不算真正的响应式。 - [05:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=354s) shouldComponentUpdate、useMemo 及并发模式(Concurrent Mode)等机制本质是虚拟 DOM 性能不足导致的抽象泄漏,迫使开发者手动干预优化。 - [06:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=400s) 框架的本质是用来组织心智而非在浏览器中组织代码的工具,将框架转移至编译阶段能兼顾声明式开发体验与原生 DOM 操作的高效执行。 - [09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) Svelte 3 摆脱了对 this 运行时绑定的依赖,直接利用原生的局部变量赋值语句(count += 1)配合编译期 invalidate 插桩驱动事件循环微任务更新。 - [09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) 在服务端渲染(SSR)中,Svelte 避开了虚拟 DOM 的构建与序列化,直接将模板编译为原生字符串拼接,显著降低服务端 CPU 开销与云成本。 - [11:42](https://www.youtube.com/watch?v=AdNJ3fydeao&t=702s) React 组件由于繁重的样板代码和手动依赖项声明,体积通常比等效的 Svelte 组件大出约 40%。 - [13:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=820s) Svelte 借用 JavaScript 中合法的标签语句语法($:),通过编译期静态依赖分析与拓扑排序,优雅实现了类似电子表格的派生状态与级联副作用。 - [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) 在极端计算负载的交互基准测试中,Svelte 凭借直接精简的更新机制保持极致流畅,胜过依赖任务切片以让出主线程的并发模式与防抖策略。 - [22:15](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1335s) 如同特斯拉电动机彻底跳出内燃机的架构限制,前端提速的核心在于彻底剔除冗余代码与抽象层,而非单纯将虚拟 DOM 逻辑切片或迁移至 WebAssembly / Web Worker。 - [25:00](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1500s) 前端新前沿已从移动端转向以 POS 机、车载系统、可穿戴设备和智能电视为代表的嵌入式 Web,这些资源匮乏的场景凸显了零运行时编译方案的刚需。 - [26:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1600s) Svelte 在编译器层面深度集成了工程规范,包括缺失 alt 属性时的无障碍(a11y)告警,以及自动哈希隔离和未引用死代码消除的 CSS 作用域机制。 - [30:29](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1829s) Svelte 的过渡动效在编译期直接生成原生 CSS @keyframes 并在独立 Compositor 线程运行,避免了传统 JS 逐帧计算占用主线程并大幅减少电量消耗。 - [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) 编译器架构打破了“框架功能越多、打包体积越大”的魔咒,Svelte 应用仅包含实际使用特性的最小代码子集,允许无负担地内置丰富动画与绑定能力。 - [34:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2046s) Svelte 生态进一步通过 Sapper(全栈 SSR 框架)、Svelte Native(移动原生应用)和 Svelte GL(轻量声明式 WebGL)向多平台延伸。 - [35:26](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2126s) Svelte 的终极愿景在于重现电子表格的普惠性,通过消解冗余样板代码,让非技术背景的产品经理与设计师能与工程师无缝协作编辑代码。 ## Detailed walkthrough ### [00:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=6s) 电子表格的历史与响应式编程的起源 演讲一开始,Rich Harris 带领大家回溯了 50 年前的历史。他幽默地营造了一种人类首次登月的宏大叙事感,描述两位勇敢的探险家迈出了一小步,不仅推动了农业、医药、商业和消费品的巨大创新,还拉近了人与人之间的距离——然而,他话锋一转,揭晓谜底:他谈论的其实是“电子表格”(Spreadsheets)。1969 年,Remy Landau 与 Rene Pardo 创立了 LANPAR Technologies。LANPAR 既是两位创始人名字的组合,也是一个首字母缩写词,代表“语言用于随机编程数组”(Language for Programming Arrays at Random)。LANPAR 奠定了现代电子表格的核心概念,其中最关键的一项创新就是“前向引用”(forward referencing),也就是今天所说的响应式编程(reactive programming)。 ```mermaid flowchart LR accTitle: 电子表格依赖关系图 accDescr: 展示单价与数量作为输入如何自动计算出小计和总计 subgraph Inputs["输入单元格"] Price["单价 (Price)"] Qty["数量 (Quantity)"] end subgraph Derived["派生计算单元格"] Subtotal["小计 (Subtotal)"] Total["总计 (Total)"] end Price --> Subtotal Qty --> Subtotal Subtotal --> Total ``` 为了让概念具象化,Rich 展示了一个记录水果购买习惯的简单表格。B 列和 C 列填入的是单价和购买数量等基础数据,而 D 列则是更有趣的公式计算。由于单元格之间建立了依赖关系,每当额外购买一个哈密瓜并修改数量时,表格内部维护的依赖关系图(dependency graph)就会驱动小计和总计自动刷新。用户甚至可以随时通过添加新公式来扩展这个有向图,让已有单元格拥有多个输出端。在 1969 年计算资源极度匮乏的背景下,这种按需更新而非盲目“全量重渲染”(re-rendering everything)的机制至关重要。 然而,LANPAR 由于理念过于超前,最终沦为计算机史上的一个脚注。世人普遍将第一款电子表格的殊荣归于 VisiCalc,Steve Jobs 甚至将 Apple II 的辉煌成功直接归功于 VisiCalc。但讽刺的是,VisiCalc 并未采用前向引用,而是采取了极其原始的做法:一旦改动某个单元格,用户必须逐个手动更新其余受影响的单元格。由于当时的用户未曾见过更好的方案,这种笨拙与低效被默默容忍。直到 Lotus 1-2-3 重新引入了前向引用机制,电子表格的功能与效率实现了质的飞跃,VisiCalc 便迅速被历史遗忘。Rich 借此点出本场演讲的主旨:从先辈的经验中汲取智慧,少做粗陋缓慢的 VisiCalc,多做高效优雅的 Lotus 1-2-3。 - 1969 年:Remy Landau 与 Rene Pardo 成立 LANPAR Technologies。 - LANPAR 全称为:Language for Programming Arrays at Random。 - VisiCalc:虽被大众与 Steve Jobs 视为 Apple II 时代的重要功臣,但缺少自动前向引用支持,依赖逐格刷新。 - Lotus 1-2-3:因实现前向引用而迅速取代 VisiCalc 成为行业标杆。 ### [02:51](https://www.youtube.com/watch?v=AdNJ3fydeao&t=171s) 响应式定义与 React 的虚拟 DOM 幻象 进入正题,Rich 探讨了“响应式编程”的本质定义。引用前一天会议中 Michel Weststrate 的发言,他戏称“有多少个响应式程序员,就有多少种响应式编程的定义”。他直接借用了其幻灯片中的严谨表述:“函数响应式编程的本质是在声明时完全指定一个值的动态行为。”换言之,响应式编程的核心在于数据流,在于跟踪数据在整个应用程序中的流转与传播;当值发生变化时,系统理应自动做出反应。 谈及主流前端生态,Rich 肯定了 React 的开创性地位,称其为改变整个 Web 开发范式的“奇迹”。自 2013 年开源以来,React 团队年复一年地攻克诸多前沿难题。但 Rich 话锋一转,明确指出业界绝不能止步于此:“我认为我们能做得更好,我认为我们能做得好得多,而这场演讲的主题就是关于如何做到这一点。”针对 JavaScript 社区中对彼此技术批评常有的局促感,Rich 坦承虽然台下正坐着 React 的核心贡献者(向台下的 Brendan 示意致歉),但他强调健康批判是技术演进不可或缺的动力,正如早年 React 拥趸对同时代框架毫不留情的批判一样。 ```mermaid flowchart TD accTitle: React 虚拟 DOM 协调比对过程 accDescr: 展示 React 遍历整个组件树比对各节点最终仅修改一个文本节点的过程 A["顶层 div 容器<br/>(无变化,保留)"] --> B["检查 class 属性<br/>(无变化,保留)"] B --> C["子节点 h1<br/>(无变化,保留)"] C --> D["h1 内部文本<br/>(无变化,保留)"] D --> E["input 输入框及值<br/>(无变化,保留)"] E --> F["button 按钮<br/>(无变化,保留)"] F --> G["按钮文本:'4' 变为 '5'<br/>(触发真实 DOM 修改)"] ``` 随后,Rich 深入剖析了 React 依赖的“虚拟 DOM”(Virtual DOM)运作原理。他展示了一个拥有 `count` 和 `name` 两个内部状态、以及处理点击与输入的常规 React 组件。每当状态变更时,React 就会重新执行组件函数,生成整块虚拟 DOM 树,然后通过 Reconciliation(协调算法)比对新旧快照。从外层的 `<div>` 容器、属性、`<h1>`、文本节点、`<input>` 一直检查到 `<button>`,比对整个节点树后,才发现仅仅是一个计数从 4 变成了 5。Rich 直言,仅仅为了把一个 4 改成 5 就耗费如此庞大的遍历检查,“作为工程师,我们应该感到被冒犯”。更糟糕的是,每当状态变化时,闭包函数都被迫反复重新声明。React 将组件内部视为一个黑盒,完全缺乏对内部数据流动粒度的感知。Rich 尖锐地总结道:“换句话说,按照任何对该词合理的定义来看,React 都算不上是响应式的。”他借用 John Lindquist 的评价:“React 这个名字对于 React.js 来说真是糟糕透顶。” - 响应式核心:“在声明时完全指定一个值的动态行为”,专注于数据流追踪与自动变化传播。 - 虚拟 DOM 比对流程:全树自顶向下递归比对标签类型、属性列表、子节点及文本节点。 - 性能瓶颈根源:组件内部状态对于运行时而言是不可见黑盒,强制引发重新求值与反复创建局部闭包。 ### [05:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=354s) 虚拟 DOM 的性能妥协与抽象泄漏 针对前端社区中常被讨论的“虚拟 DOM 究竟快不快”这一话题,Rich 给出了毫不留情的定论:“不,它并不快。”如果退一步追问“虚拟 DOM 是否足够快”,针对数据可视化和复杂动画这类对帧率与延迟极其严苛的场景,他的回答依然是绝对的“不够快”。他指出,甚至不需要相信他个人的主观论断,React 核心团队自身的工程演进就已经提供了最有力的证据。 Rich 论证道,正是因为虚拟 DOM 本身不够快,官方团队才不得不提供诸如 `shouldComponentUpdate`、`React.PureComponent`、`useMemo` 和 `useCallback` 等机制。这些本质上全部是“抽象泄漏”(abstraction leaks)。它们强迫开发者在编写业务逻辑的同时,手动充当优化器,介入并明确告知计算机“跳过检查这颗子树”。Rich 一针见血地指出:“换句话说,你是在替计算机干它原本该干的工作。”不仅如此,社区开始投入大量精力构建像并发模式(Concurrent Mode)这样复杂的摊销策略(amortization strategies),这同样从反面印证了虚拟 DOM 在处理计算密集型更新时的性能瓶颈。 - 抽象泄漏代表 API:`shouldComponentUpdate`、`React.PureComponent`、`useMemo`、`useCallback`。 - 开发者负担:不得不手动标记依赖与优化条件,侵入业务心智模型。 - 架构补丁:引入 Concurrent Mode 等任务切片与摊销算法,从架构层面侧面证实了底层全量调度的开销压力。 ### [06:39](https://www.youtube.com/watch?v=AdNJ3fydeao&t=399s) 框架即编译器:Svelte 3 响应式的范式转移 面对既要 React 式声明式组件开发的开发体验(DX),又要原生精准操作真实 DOM 的极致性能(UX)的双重诉求,Rich 重新审视了框架的本质:“框架并不需要成为一个在浏览器中运行的东西。框架不是用来组织代码的工具,它们是用来组织心智的工具。”基于这一认知,框架理应转移至构建阶段(build step)执行。这呼应了前一天 Kyle 所呼吁的观点:“编译器才是全新的框架(Compilers are the new frameworks)。” Rich 由此创立了 Svelte。Svelte 的编译管线接收开发者编写的声明式组件,并在构建期直接将其翻译为高效、命令式且细粒度操控原生 DOM 的底层 JavaScript 代码。在解决“如何判断什么发生了改变”这一核心问题上,行业经历了数次演进: ```mermaid flowchart LR accTitle: 状态变更触发机制的演化 accDescr: 展示从类组件方法调用、Hooks 函数调用到编译器赋值监听的技术迭代 subgraph RuntimeAPIs["基于运行时的 API"] ClassStyle["React 类组件<br/>(this.setState)"] SvelteEarly["早期 Svelte<br/>(this.set)"] HooksStyle["React Hooks<br/>(setCount(c => c + 1))"] end subgraph CompilerApproach["基于编译器的语言级方案"] SvelteThree["Svelte 3<br/>(count += 1)"] end ClassStyle -.->|"摆脱 class 束缚"| HooksStyle ClassStyle -->|"抛弃 this 运行时依赖"| SvelteThree SvelteEarly -->|"编译为底层 DOM 操作"| SvelteThree ``` 早期的 React 类组件采用 `this.setState`,合并新旧状态并触发整树重渲染;现代 React 则转向 Hooks,通过调用如 `setCount` 这样的派发函数更新状态变量;早期 Svelte 同样采用类似 `this.set` 的形式。然而,这些方案的共同弊病在于极其繁琐,且无一例外深度依赖运行时的 `this` 绑定,引入了难以预测的代码约束。 Rich 进而发问:“我们到底该如何告诉计算机数据发生了改变?”其实编程语言自身早就给出了最自然的答案——赋值操作符(Assignment Operator)。最简洁明了的表达莫过于声明一个局部变量后,直接书写 `count += 1`。尽管 Vue 很早就通过属性赋值(`this.count += 1`)利用 Object.defineProperty/Proxy 在运行时捕获更新,但它依然逃不开 `this` 上下文的运行时束缚。 Rich 总结道,普通的 JavaScript 规则或许限制了运行时框架的发挥,“但我们是一个编译器,我们不需要遵守其他人的规则”。即将发布的 Svelte 3(或在观众看到视频时已经发布)正是实现了这样一种飞跃:它彻底将响应式从运行时的组件 API 中剥离出来,直接融入到了编程语言的表意之中。 - 框架本质重构:“框架不是用来组织代码的工具,它们是用来组织心智的工具。” - 编译器路径:在打包构建阶段直接生成直接操纵 DOM 的底层命令式代码,剔除庞大框架 Runtime 开销。 - 状态更新范式转变:摆脱 `this.setState()` 或 `setCount()` 等 API 范式,直接利用 JavaScript 原生赋值语法 `count += 1` 作为响应式触发点。 ### [09:24](https://www.youtube.com/watch?v=AdNJ3fydeao&t=564s) 编译期插桩与轻量级服务端渲染(SSR) Rich Harris 通过一个简单的计数器示例展示了 Svelte 的底层工作机制。在 Svelte 中,无需像 React 那样引入 `useState`,只需直接声明普通变量 `let name = 'world'` 和 `let count = 0`,并在事件触发时通过标准的赋值操作 `count += 1` 即可更新状态。当查看编译器输出的代码时,可以发现开发者的原始代码几乎被完整保留,编译器所做的仅仅是在赋值语句周围插入了微小的插桩代码——`invalidate` 函数。 ```mermaid flowchart TD accTitle: Svelte 响应式插桩流程 accDescr: 展示变量被赋值时触发 invalidate 并调度批处理更新的过程 subgraph UserCode["用户编写的代码"] Assign["赋值操作: count += 1"] end subgraph Invalidation["编译器注入机制"] InvCall["调用 $$invalidate(...)"] Notify["标记 count 发生变更"] Schedule["通知组件在当前 Event Loop 末尾批量更新 DOM"] end Assign --> InvCall InvCall --> Notify Notify --> Schedule ``` Rich Harris 指出,`invalidate` 的核心职责非常明确:“它的工作就是告诉组件:这个值已经改变了,在更新时请注意;顺便提一句,你需要在当前事件循环(Event Loop)的末端进行更新。”此外,Svelte 还通过 `bind:` 指令省去了手动编写事件监听器来同步输入框与变量的繁琐模版代码。将 HTML 与 JavaScript 的编写逻辑倒置(在 HTML 中写 JavaScript,而非在 JavaScript 中嵌 HTML),还带来了一项关键优势:编译目标高度灵活。 - **SSR 字符串拼接加速**:在服务端渲染模式下,Svelte 不会构建虚拟 DOM 组件树再进行序列化,而是直接将模版编译为字符串拼接代码,执行速度比虚拟 DOM 快数个数量级。 - **降低云服务成本**:Rich Harris 开玩笑建议大家向产品经理推销 Svelte,因为“如果你用 Svelte 做服务端渲染,你的 AWS 账单都会变少”。 - **一致的模块使用方式**:Svelte 组件像普通 JavaScript 模块一样被 `import`。在客户端通过 `new App(...)` 实例化生成真实 DOM,而在服务端调用 `App.render(...)` 直接输出 HTML 字符串。 --- ### [11:42](https://www.youtube.com/watch?v=AdNJ3fydeao&t=702s) 派生状态的困境:React 重新执行与 useMemo 的代价 绝大多数组件不仅包含独立状态,还包含依赖其他状态计算得来的“派生状态(derived state)”,这与电子表格中由其他单元格求和的公式单元格如出一辙。Rich Harris 演示了一个包含筛选逻辑的 Todo 列表应用,用户可以标记任务完成状态,并选择“隐藏已完成项”。在 React 中,派生状态通常直接在函数体中重新执行计算(如 `const filtered =...`),然后通过 `.map()` 渲染成列表。 然而,一旦该组件嵌套在父组件内,父组件重新渲染会导致子组件内部所有未加保护的变量被无意义地重复计算。React 给出的解决方案是将计算逻辑包裹进 `useMemo`,并显式传入依赖项数组:`useMemo(() =>..., [todos, hideDone])`。React 会在底层逐项浅比较当前依赖项与上一轮的值,若无变化则返回缓存结果。但这给开发者带来了沉重的心智负担与样板代码。 - **React 代码量显著膨胀**:Rich Harris 在将大量 React 组件重写为 Svelte 时统计发现,“通常情况下,React 组件大约要大出 40%,你需要多敲 40% 的代码才能达到相同的功能。” - **Svelte 初始设计的短板**:如果直接在 Svelte 脚本中写 `const filtered =...`,由于顶层代码仅在组件挂载时执行一次,点击切换筛选时派生变量并不会随之更新,必须寻找全新的响应式语法机制。 --- ### [13:40](https://www.youtube.com/watch?v=AdNJ3fydeao&t=820s) 从 Observable 拓扑排序到 Destiny 运算符与标签声明 为了解决派生状态的响应问题,Rich Harris 借鉴了数据可视化领域的先驱 Mike Bostock(D3.js 与 Observable 笔记本的创造者)。Observable 与常规从上到下执行的 Jupyter 或 RunKit 笔记本不同,它是按照“拓扑顺序(topological order)”运行的:只要语句 A 依赖于语句 B,无论二者在代码中声明的物理顺序如何,语句 B 永远先于语句 A 执行,这本质上就是电子表格的工作模式。 澳大利亚开发者 Paul Stovell 曾在博文《什么是响应式编程》中虚构过一个 2051 年代码考古学家的故事:未来的程序员无法理解为何当前代码中 `var a = 10; var b = a + 1;` 之后修改 `a`,`b` 却不能自动更新,而在未来他们使用“命运运算符(Destiny Operator)”`var b:= a + 1`,将两者的关联永久绑定。虽然自定义操作符会破坏 JavaScript 语法合法性并导致 Babel、ESLint 等工具链失效,但 Rich Harris 发现了一个在语言标准中长期被遗忘的合法语法——标签语句(labeled statement)。 ```mermaid flowchart LR accTitle: 响应式派生计算的拓扑更新链条 accDescr: 展示 todos 或 hideDone 变动后,先计算 filtered 再计算 showing 的顺序 State["基础状态: todos / hideDone"] Filtered["$: filtered =..."] Showing["$: showing = filtered.length"] ConsoleLog["$: console.log(showing)"] State -->|"触发更新"| Filtered Filtered -->|"依赖计算"| Showing Showing -->|"依赖副作用"| ConsoleLog ``` - **重用标签语句**:Svelte 借用了在 JavaScript 规范中几乎极少被用到的 `$` 标签(`$: b = a + 1`),让其承担类似命运运算符的响应式绑定功能。 - **级联响应与任意副作用**:开发者不仅可以写 `$: showing = filtered.length` 形成派生链条,还可以直接写 `$: console.log(filtered)` 执行副作用,极大简化了运行时调试。 - **编译器拓扑分析输出**:Svelte 编译器会静态解析依赖关系。在生成的 JavaScript 代码中,这些语句被包裹在 `if (changed.hideDone || changed.todos)` 条件分支中,并且严格按照拓扑顺序排列,优先重新计算 `filtered` 并调用 `invalidate`,再执行依赖它的 `showing` 计算。 --- ### [17:09](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1029s) 魔法争论与编译期边界:“SvelteScript”的哲学 这套利用标签语法的响应式方案公布后,在社区引发了不小的关于“黑魔法”的争吵。Vue.js 的作者尤雨溪(Evan You)曾直言评价道:“这根本就不是 JavaScript,严格来说这是 SvelteScript。”Rich Harris 坦然承认:“他是对的,我们确实是在‘为了乐趣和利益滥用这门语言’(abusing the language for fun and profit)。” 但 Rich Harris 同时反驳称,尤雨溪本人正是让“通过对象赋值在 JavaScript 中触发副作用”这一理念普及开来的关键人物(甚至超过了 MobX 的 Michel Weststrate)。尤雨溪划定的分界线是“浏览器运行时的魔法”与“编译阶段的魔法”,而在 Rich Harris 看来,这种人为划分是站不住脚的: > “我认为这是一种武断的人为区分。如果你在两者之间划清界限,你就犯了和过去那些坚持把模板、样式和 JavaScript 分开写在不同文件里的人一样的错误,尽管它们明明属于同一个组件。真正重要的是功能本身,而不是功能在哪里发生。” --- ### [18:04](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1084s) 基准测试的荒谬性与 React Concurrent 模式 进入性能对比环节时,Rich Harris 调侃了自己的一个“恶趣味”:每当其他框架作者发布基准测试宣称自己的框架有多快时,他总会把 Svelte 加入对比跑一遍,然后把结果发到 Twitter 上,并打趣道:“这大概就是我没什么朋友的原因,但这绝对值回票价。”在一项早期基准测试中,Svelte 的速度甚至达到了 React 的 35 倍、Vue 的 50 倍。 然而,React 核心团队的 Dan Abramov 认为这类纯速度基准测试毫无实际意义。Rich Harris 对此表示认同:如果显示器的物理刷新率只有 60Hz,让框架每秒更新几十万次没有任何用户层面的价值,真正需要衡量的是实际的用户体验(User Experience)。Dan Abramov 当时所力推的正是为解决 UI 响应与交互卡顿而设计的 React 新特性——Concurrent 模式(并发模式)。Rich Harris 引入了 Dan Abramov 在 JSConf Iceland 演讲中的经典 Demo 来剖析现状:在当时的 React 应用中,当应用负荷较重时,用户在输入框中连续打字会感受到明显的卡顿与按键延迟,界面的即时反馈被严重阻塞。 ### [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) 并发模式对比 Svelte:优化瓶颈还是换掉引擎? Rich Harris 在现场演示中测试了用户界面在极端计算压力下的表现。在没有优化的场景中,界面的“挫败度指示器”(frustration meter)频繁告急,打字变得极不流畅;而传统的防抖(debouncing)策略虽然能释放主线程保证打字响应,却导致打字期间界面没有任何视觉反馈,停止打字后才一次性全量更新,用户体验极差。相比之下,当时被称为异步模式(Async Mode)或并发模式(Concurrent Mode)的技术在打字时提供了折中方案:当计算负荷过重时将任务切片(chunk up the work)并主动让出主线程,使得挫败度指示器全程保持绿色;但随着复杂度攀升,后台图表的渲染明显开始卡顿。 随后 Rich Harris 展示了使用 Svelte 构建的同款应用。Svelte 的更新不仅行云流水,挫败度指示器也完全处于良好状态,无需依赖繁复的时间切片(time slicing)就提供了天壤之别的流畅体验。谈及其他底层采用 Virtual DOM 机制的高性能框架(例如 Inferno),Rich 指出它们可能表现会优于 React,但本质上仍受限于 Virtual DOM 的架构瓶颈。在切回 React 并发模式演示其逐渐变卡(chunky)的现象后,Rich Harris 犀利地总结道:“以优异体验交付用户的最佳方式,就是极致的快速。” > “The best way to deliver a good user experience is to be extremely fast.” —— Rich Harris ```mermaid flowchart TD accTitle: 界面响应更新策略对比 accDescr: 对比传统防抖、React并发时间切片和Svelte直接编译更新三种模式在输入与渲染上的行为差异。 subgraph Debounce["传统防抖 (Debouncing)"] D1["连续输入"] --> D2["主线程空闲但无UI反馈"] D2 --> D3["打字停止后一次性大更新"] end subgraph TimeSlicing["React 并发 / 时间切片"] T1["连续输入"] --> T2["切分VDOM计算任务"] T2 --> T3["让出主线程保持响应"] T3 --> T4["复杂场景下后台渲染逐渐迟滞"] end subgraph SvelteApproach["Svelte 编译式响应"] S1["连续输入"] --> S2["无 Virtual DOM 开销"] S2 --> S3["直接、极轻量的精确 DOM 变更"] S3 --> S4["高负载下仍保持丝滑流畅"] end ``` --- ### [22:15](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1335s) 内燃机与电动机的隐喻:代码搬家与重写的误区 Rich Harris 借用内燃机(Internal Combustion Engine)与特斯拉电动机的类比阐明了底层架构创新的重要性。汽车厂商耗资数百万美元在极其复杂的内燃机上压榨零点几个百分点的增量收益;顶尖内燃机包含成百上千个活动部件,能量转换效率仅为 20% 到 25% 左右。而特斯拉跳出传统化石燃料的假设框架,其电动机仅有两个活动部件,能量效率却高达 85%。当下前端领域的很多优化正如汽车厂商死磕内燃机一样,陷入了在旧假设上做无用功的误区。 常见的性能优化误区包括: - **时间切片(Time Slicing)**:把代码拆细搬来搬去,或许能让感官变快,但根本没有减少总计算量。 - **Web Workers**:几年前开发者普遍试图将业务代码全扔进 Web Worker 释放主线程,但实践证明此路不通,单纯改变代码运行环境无法带来质的飞跃。 - **Rust / WebAssembly 重写**:Nick Fitzgerald 曾发布用 Rust 编写并编译为 Wasm 的 Virtual DOM 库 Dodrio,并宣称具备“同类最优(best-in-class)”性能。Rich Harris 将 Svelte 加入该 Benchmark 测评,结果 Svelte 性能远超 Dodrio。在将基准测试中原本用于调度的 `setTimeout`(大部分时间只是在等待浏览器定时器)替换为 Promise resolution 后,直接测试原生 DOM 更新能力,Svelte 以更大的优势胜出。 Rich 强调自己十分看好 Rust 和 Wasm 的前景,但核心问题在于 Virtual DOM 机制本身就存在无法逃避的性能天花板。要让代码真正变快,唯一可靠的法则就是减少代码本身,像近藤麻理惠(Marie Kondo)整理房间一样,问问自己:“这段 JavaScript 代码真的能带来快乐吗?” > “There's only one reliable way to speed up your code and that is to get rid of it.” —— Rich Harris --- ### [25:00](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1500s) 真实世界的算力挑战:嵌入式 Web 与 Svelte 落地案例 许多人指责讨论底层性能的开发者“好高骛远(heads in the clouds)”、脱离现实业务需求,Rich Harris 对此反驳称,性能正是最切合现实(real-world)的核心指标。主流移动设备具有强大的 CPU 和充足的内存,但在资源极度匮乏的设备上,传统重型框架立刻原形毕露: - **巴西金融科技公司 Stone**:其业务是生产供刷卡使用的 POS 机系统。团队曾尝试用 React、Vue 等一整套主流框架来开发设备界面,但运行速度极其缓慢、体验糟糕;最终他们改用 Svelte 彻底解决了性能问题。如今巴西街头有超过 200,000 台刷卡机在低配硬件上流畅运行着 Svelte。 - **俄罗斯多媒体公司 Most Lab**:专注于智能电视(Smart TV)等大屏应用的开发,这类设备性能受限且需要高效产出,Svelte 成为绝佳之选。目前他们已将 Svelte 推广到包括智能家居自动化控制面板在内的更多业务板块。 Rich 明确指出,移动端 Web 早已不再是技术前沿,新的阵地是嵌入式 Web(Embedded Web)——涵盖可穿戴设备、物联网(IoT)、智能电视以及车载娱乐系统。在这些低算力设备上,传统以 JavaScript 运行时为中心的框架根本无法达标,行业需要更精简高效的编译时工具。 > “The mobile web is not the new frontier anymore; the new frontier is the embedded web: wearables, Internet of Things, smart TVs, in-car entertainment, all that kind of stuff, and the traditional JavaScript-first framework simply will not cut the mustard.” —— Rich Harris --- ### [26:38](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1598s) 内置工程化利器:无障碍审查与组件作用域样式 在阐述完性能后,Rich Harris 转向 Svelte 带来的其他开发体验红利。首先是无障碍支持(Accessibility, a11y)。很多开发者由于自身没有视力或运动功能障碍,往往在潜意识中忽视无障碍优化,而工具链层面的介入能有效改变这一现状。在 Svelte 中,编写不合规范的标签会直接触发编译告警。例如为一个 `<img>` 标签指定了 `src` 却遗漏了 `alt` 属性,无论是在网页端还是终端构建时,Svelte 都会明确报出 `image element should have an alt attribute`。虽然代码仍可编译,但正如 Rich 所调侃的:“它允许你写出无障碍不合格的标记,但它打心底瞧不起你。” ```mermaid flowchart LR accTitle: Svelte 样式作用域与死代码清理流程 accDescr: 展示Svelte编译器如何自动将组件样式隔离,并剔除未匹配标记的冗余CSS。 subgraph CompileStage["Svelte 编译器分析"] M["组件 HTML 标记"] S["组件 <style> 标签"] M & S --> Comp{"解析与哈希生成"} end subgraph OutputStage["构建产物输出"] Comp -->|"注入唯一 Class Hash"| Scoped["作用域隔离的 CSS & DOM"] Comp -->|"标记与样式选择器不匹配"| Warn["输出未引用警告 & 自动剔除 CSS 代码"] end ``` 在样式处理方面,几乎没有任何现代应用会毫无样式,但传统框架往往不自带样式解决方案,需要借助 CSS-in-JS 或复杂的 BEM 命名体系。Svelte 作为 HTML 的超集,直接原生支持组件内部的 `<style>` 标签: - **精确的作用域隔离**:组件内写 `p { color: red; }` 只会命中当前组件的段落,绝不污染子组件或外部元素。其底层原理是编译器自动为模板节点与 CSS 选择器添加独一无二的计算哈希 Class。 - **未引用样式的编译检测**:如果写了选择器却没有匹配当前组件的任何标记,Svelte 会发出警告并在打包写入磁盘时直接剔除该段样式,根治了开发者“不敢随便删历史样式表”的行业通病。 在演示的最后,Rich 还展示了开箱即用的声明式过渡动效系统。只需从 `svelte/transition` 导入预设动画并在节点上声明类似 `transition:fly` 及其配置参数,布尔状态切换时节点便能平滑出入,避免了突兀闪现。 > “It will let you write markup that isn't accessible, but it won't respect you for it.” —— Rich Harris ### [30:29](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1829s) Svelte 的动画引擎:脱离主线程的 CSS 过渡与自定义缓动 Svelte 能够平滑地反转正在进行的过渡效果,并让元素精准返回应处的位置,这种能力在许多现代前端框架中往往实现起来相当棘手。这套机制最具突破性的地方在于:它在运行时并非依赖庞大的 JavaScript 循环来驱动帧渲染。当组件状态发生改变时,Svelte 会在幕后预先计算出一段 CSS `@keyframes` 动画并直接挂载到 DOM 元素上。由于现代浏览器的 CSS 动画可以直接交由 Compositor 线程处理而脱离主线程(Off the main thread),这种架构不仅大幅提升了运行流畅度,更对移动设备的能耗与电池寿命极为友好。 ```mermaid flowchart LR accTitle: Svelte 动画与过渡生成机制 accDescr: 展示从 JavaScript 缓动函数定义到编译为原生 CSS 关键帧并交由浏览器合成线程渲染的全流程。 subgraph Definition ["开发者定义"] direction TB A["自定义过渡函数<br/>spin(node, options)"] --> B["返回 CSS 计算函数<br/>css(t) 映射 0 到 1"] end subgraph Compiler ["Svelte 编译处理"] direction TB C["计算补间帧并生成<br/>CSS @keyframes"] end subgraph Execution ["浏览器渲染"] direction TB D["注入样式并挂载动画"] --> E["Compositor 独立线程执行<br/>(不阻塞 JS 主线程、省电)"] end Definition --> Compiler Compiler --> Execution ``` 开发者可以利用该机制自由实现高度个性化的自定义过渡动画(Custom transitions)。在演示中,Rich Harris 展示了一个自定义的 `spin` 函数:该函数接收目标 DOM 节点 `node` 及可选配置参数,返回一个包含 `css` 方法的对象;该方法接收参数 `t`(取值范围为 `0` 到 `1`,`0` 代表完全离屏,`1` 代表完全进场),并在内部计算并返回对应的 CSS 样式属性字符串。Svelte 编译器随后接手这段逻辑,将其编译为完全原生的 CSS 动画。这样一来,开发者不仅能将离场动作设置为常规的 `out:fade`,还能将进场动作指定为极其花哨的 `in:spin`,甚至随意引入传统 CSS 无法原生支持的高级数学缓动函数(Easing functions),且完全无需承受任何 JavaScript 运行时动画的性能惩罚。 - `spin` 自定义过渡函数的参数规范:接收 DOM `node` 与配置选项,返回包含 `css(t)` 的对象,其中参数 `t` 严格映射元素在 `0`(完全离屏)与 `1`(完全进场)之间的生命周期。 - 架构执行差异:传统框架多依赖 JS 的 `requestAnimationFrame` 循环修改内联样式并持续占用主线程;Svelte 将动画计算前置为 CSS,由浏览器脱离主线程独立调度。 --- ### [31:54](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1914s) 物理直觉驱动的 UI:列表交叉移动与解耦体积的功能集 在展示待办事项列表(To-do list)案例时,界面分为“未完成”与“已完成”两个不同视图。在传统实现中,点击勾选一个项目后,该项目会瞬间从上方列表凭空消失,并立刻从下方列表凭空出现。Rich Harris 指出这种生硬的视觉体验极其违反现实物理规律:“That’s quite unsatisfying because that’s not how objects behave in the real world. Our brains don’t like it when things just disappear and reappear elsewhere on the screen.” 人脑需要界面元素优雅、平滑地移动到新位置,以帮助用户直观理解数据在应用内部的状态变迁。在 Svelte 中,仅需向待办标签元素添加简单的过渡指令,条目就会像真实物理对象一样在两个列表之间平滑移动并腾出空间,而这一整套复杂的交叉补间移动同样完全由轻量化的 CSS 完成。 大多数传统前端框架通常不会内置此类过渡和动画功能,其核心瓶颈在于对打包体积(Bundle size)的顾虑。在 JavaScript 生态中,各大框架在营销主页上往往大肆宣扬极简体积,例如标榜自己是“3KB 替代 React 的轻量方案”;为了守住体积底线,它们不得不阉割内置功能,迫使开发者去寻找并引入第三方库。然而,Rich Harris 强调:“Svelte is a compiler. It doesn’t have the same constraints.” 编译器架构彻底打破了“功能丰富度与文件体积绑定”的宿命:如果某个应用完全没用到过渡动画(Transitions)或双向绑定(Bindings),编译器生成的最终代码包中就绝不会包含任何相关运行逻辑。 - 传统运行时框架的矛盾:要在框架核心内提供丰富功能,就必须把未使用的特性代码一并打包下发给用户,导致打包体积急剧膨胀。 - 编译器解耦机制(Decoupling feature set from file size):Svelte 能够极度激进地拓展内置特性的边界,因为开发者始终只会得到应用所依赖特性的绝对最小代码子集(Minimal subset),实现“免费享受全套能力且不为未用功能买单”。 --- ### [34:06](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2046s) Svelte 生态演进:从底层组件到 Sapper、Svelte Native 与 Svelte GL Svelte 本质上是一个底层的组件级框架,虽然足以构建非常复杂的应用,但当工程规模达到一定程度时,开发者必然需要一套更完善的应用级全栈解决方案。为此,官方团队打造了配套项目 Sapper。如同 React 生态中的 Next.js 或 Gatsby,Sapper 将底层的 Svelte 组件模型扩展为全功能的应用框架,原生集成了路由系统(Routing)、代码分割(Code splitting)以及服务端渲染(SSR)。与现有方案相比,得益于 Svelte 的编译时优化,Sapper 生成的客户端 JavaScript 体积被大幅压缩。 在组件与应用框架之外,Svelte 的编译思想正在向更多终端和计算平台延伸: 1. **Svelte Native**:一个基于 NativeScript-Vue 衍生的社区驱动项目,允许开发者使用 Svelte 的声明式语法直接构建原生的 iOS 和 Android 原生应用程序,未来计划转为官方维护项目。 2. **Svelte GL**:Rich Harris 重点展望了一个正在构想中的项目。它类似于 Three.js,但采用 Svelte 特有的轻量编译哲学:开发者利用 Svelte 的声明式语法来描述三维场景图(3D scene graph),随后编译器直接将其编译并映射到底层纯粹的 WebGL 绘制调用(Draw calls),从而彻底抛弃传统三维图形库那层沉重的运行时封装与代码膨胀。 --- ### [35:26](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2126s) 像电子表格一样普惠:打破专业壁垒的开发愿景 Rich Harris 将演讲主题重新拉回电子表格(Spreadsheets),并揭示了它最令人震撼的核心特质——普惠性与无与伦比的可访问性(Accessibility)。他坦言自己的妻子并非程序员,且在两人共同生活了 15 年后也丝毫没有想成为程序员的念头,但她却能利用电子表格完成许多令人不可思议的复杂工作。每当 Rich 看到妻子使用表格处理业务时,都会为全球无数企业仅凭这套看似不起眼的网格系统创造出的巨大商业价值而感到震撼。 “Wouldn't it be wonderful if the tools that we used to build the web were as accessible as spreadsheets are?” Rich Harris 明确指出,Svelte 最核心的终极愿景之一,就是在不牺牲专业用户所需强悍性能的前提下,让 Web 开发变得像电子表格一样人人可用。他分享了一个来自 Spotify 的真实故事:他的朋友 Eliza 在 Spotify 使用 Svelte 推进项目时,非技术背景的产品经理(PM)竟然能够直接走到电脑旁,与工程师一起看懂并直接修改组件代码;而面对充斥着样板代码和复杂抽象的传统 JavaScript 文件,这往往是无法想象的。 演讲最后,Rich Harris 深情总结道,当具备不同专业背景、不同技能树的人们能够毫无芥蒂地聚在同一台笔记本电脑前,实时协同修改同一段代码时,真正伟大的事情就会发生。他引用这一场景作为全场收尾:“So I hope that one day we'll be able to look back at Svelte and say that in a strange way it brought us all a little closer together.” - 跨专业协同门槛:传统框架充斥着闭包、副作用钩子与运行时样板代码,排除了非技术职能人员的参与;Svelte 贴近标准 HTML/CSS/JS 的编译语法让产品经理与设计师也能直接阅读和修改。 - 核心愿景落地:在极端性能、极小体积与普惠开发体验之间取得统一,让编程工具重归如电子表格般易懂、即时响应且富有表现力。 ## Notable quotes > [06:44](https://www.youtube.com/watch?v=AdNJ3fydeao&t=404s) 框架不是用来组织代码的工具,它们是用来组织心智的工具。 — Rich Harris > [22:15](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1335s) There's only one reliable way to speed up your code and that is to get rid of it. — Rich Harris > [19:35](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1175s) The best way to deliver a good user experience is to be extremely fast. — Rich Harris > [02:51](https://www.youtube.com/watch?v=AdNJ3fydeao&t=171s) 按照任何对该词合理的定义来看,React 都算不上是响应式的。 — Rich Harris > [17:09](https://www.youtube.com/watch?v=AdNJ3fydeao&t=1029s) 真正重要的是功能本身,而不是功能在哪里发生。 — Rich Harris > [06:44](https://www.youtube.com/watch?v=AdNJ3fydeao&t=404s) 编译器才是全新的框架。 — Kyle > [35:26](https://www.youtube.com/watch?v=AdNJ3fydeao&t=2126s) Wouldn't it be wonderful if the tools that we used to build the web were as accessible as spreadsheets are? — Rich Harris > [02:51](https://www.youtube.com/watch?v=AdNJ3fydeao&t=171s) 函数响应式编程的本质是在声明时完全指定一个值的动态行为。 — Michel Weststrate ## Mentioned Rich Harris, Remy Landau, Rene Pardo, LANPAR Technologies, VisiCalc, Steve Jobs, Apple II, Lotus 1-2-3, Michel Weststrate, React, Brendan, John Lindquist, Kyle, Svelte, Vue.js, Mike Bostock, D3.js, Observable, Paul Stovell, 尤雨溪 (Evan You), Dan Abramov, Inferno, Tesla, Nick Fitzgerald, Dodrio, Rust, WebAssembly, Stone, Most Lab, Sapper, Next.js, Gatsby, Svelte Native, NativeScript-Vue, Svelte GL, Three.js, Spotify, Eliza ## Video description Modern JavaScript frameworks are all about reactivity. Change your application's state, and the view updates automatically. But there's a catch — tracking state changes at runtime adds overhead that eats into your bundle size and performance budgets. In this talk, we'll discover an alternative approach: moving reactivity into the language itself. Your apps have never been smaller or faster than they're about to become. Slides: https://rethinking-reactivity.surge.sh/ YGLF 2019: Code Camp Blog - Rich Harris: https://www.israel.yglfconf.com/post/meet-the-speaker-rich-harris [Source](https://www.youtube.com/watch?v=AdNJ3fydeao)

Source quotes

[6:44] Rich Harris: 框架不是用来组织代码的工具,它们是用来组织心智的工具。 [22:15] Rich Harris: There's only one reliable way to speed up your code and that is to get rid of it. [19:35] Rich Harris: The best way to deliver a good user experience is to be extremely fast. [2:51] Rich Harris: 按照任何对该词合理的定义来看,React 都算不上是响应式的。 [17:09] Rich Harris: 真正重要的是功能本身,而不是功能在哪里发生。 [6:44] Kyle: 编译器才是全新的框架。 [35:26] Rich Harris: Wouldn't it be wonderful if the tools that we used to build the web were as accessible as spreadsheets are? [2:51] Michel Weststrate: 函数响应式编程的本质是在声明时完全指定一个值的动态行为。

Source quotes

[6:39] Rich Harris: Frameworks are not tools for organizing your code; they are tools for organizing your mind. [19:35] Rich Harris: The best way to deliver a good user experience is to be extremely fast. [31:54] Rich Harris: Developers, as developers, we want all the features, but we just don't want to pay for the ones that we're not using, and what's good for developers is ultimately good for users.

Original transcript

[0:06] 50 years ago humanity entered a new era [0:10] when two heroic explorers took a small [0:12] step into a new world it was a massive [0:15] technolog technological achievement [0:17] aside from the breakthrough itself it [0:19] spurred innovation in agriculture and [0:21] medicine business and consumer products [0:23] and in a strange way it brought us all a [0:25] little closer together I'm talking of [0:28] course about spreadsheets 1969 was the [0:31] year that Remmy Landau and Rene Pardo [0:34] came together to form lampur [0:36] technologies as well as being a play on [0:38] their names LAN par is an acronym it [0:40] stands for the language for programming [0:42] arrays at random and it had a lot of the [0:44] features that we associate with modern [0:45] spreadsheets most notably forward [0:47] referencing or as we call it reactive [0:50] programming and you've all experienced [0:52] this but I'm going to show you anyway I [0:53] have a spreadsheet here that tracks my [0:55] fruit purchasing habits and in columns B [0:57] and C I have some values I've got some [0:59] prices and I've got some quantities in [1:02] column B I've got something more [1:03] interesting I've got formulas the values [1:05] of these cells are derived from the [1:07] values of other cells which means that [1:09] if I buy another cantaloupe the [1:11] spreadsheet updates automatically the [1:13] subtotal and the total updates reflect [1:15] the new inputs and the way that this [1:17] works is the spreadsheet maintains a [1:19] dependency graph internally and we can [1:21] visualize that we can click around and [1:24] we can see the inputs and the outputs [1:25] for each of these cells and we can even [1:27] change a dependency graph by adding a [1:28] new formula [1:33] all of a sudden these cells now have two [1:36] outputs now this was important back in [1:38] 1969 because computers weren't very fast [1:40] then it's a lot more efficient to only [1:42] update the things that have changed [1:43] rather than re-rendering [1:45] everything but lamp r wasn't destined to [1:48] become a household name it was too far [1:51] ahead of its time and instead it became [1:53] a footnote in the history of computers [1:55] nowadays when we think about the first [1:57] electronic spreadsheet we think about [1:58] busy count Missi count was a huge deal [2:01] Steve Jobs credited busy count with it [2:03] with the success of the Apple 2e [2:05] pictured here the visicalc didn't use [2:08] forward referencing it took a far more [2:10] naive approach every time you changed [2:12] the data in one of your cells you had to [2:14] go cell by cell [2:15] updating all the others and it was [2:17] pretty slow and clunky people didn't [2:19] mind they didn't know any better but it [2:20] meant that when Lotus 1-2-3 came along [2:22] which did implement forward referencing [2:24] spreadsheets suddenly became so much [2:26] more capable you could use them for more [2:28] things and in a very short period of [2:30] time [2:30] busy count was all but forgotten my name [2:34] is rich Harris I am a graphics editor at [2:36] the New York Times and this talk is [2:37] called rethinking reactivity it's a talk [2:40] about how we can apply the lessons of [2:41] our forebears to build software that is [2:43] a little bit less busy out and a little [2:45] bit more Lotus 1-2-3 and I hope you [2:47] enjoy it what is reactive programming [2:51] well this Michelle said yesterday there [2:54] are as many definitions of reactive [2:56] programming is there are reactive [2:57] programmers so to make it simple I just [2:59] stole his from his slides the essence of [3:01] functional reactive programming is to [3:03] specify the dynamic behavior of a value [3:05] completely at the time of declaration in [3:07] other words reactive programming is all [3:08] about beta flow it's all about tracking [3:10] values through your application when the [3:13] value changes your application should [3:15] react we've talked about reacts a lot of [3:18] this conference already and we're going [3:19] to talk about it a little bit more react [3:21] is a miracle that changed everything [3:23] whether you use it or not you've [3:25] probably been influenced by the patterns [3:27] that react introduced to the web [3:28] programming community and since it was [3:31] released in 2013 the react team has [3:33] crushed it year after year helping us [3:34] solve problems that we didn't even know [3:36] that we had but I think we can do better [3:39] I think we can do a lot better and this [3:41] talk is going to be about how now I know [3:44] that in the JavaScript community we tend [3:46] to get a little bit uncomfortable [3:47] when we criticize each other's work [3:49] especially when I react contributor is [3:50] sitting right in front of me sorry [3:52] Brendan but I think it's really [3:53] important for our collective growth and [3:55] if you look back to the early days when [3:57] react first came out and react advocates [3:59] were giving talks at conferences they [4:01] were very very critical about the [4:03] frameworks of that era and rightly so [4:05] I'm going to continue in that proud [4:07] tradition react was the first library [4:10] that used a virtual Dom and we're going [4:13] to talk about how virtual Dom libraries [4:14] work a lot of you might already know [4:17] what this code does but let's talk [4:19] through it anyway this is a simple react [4:22] application that has a single component [4:23] called app and it has some local state [4:25] account and name and it has two [4:28] functions that change that state handle [4:29] click which use this set count and [4:31] handle input which use this set name and [4:33] then at the bottom it returns a slice of [4:35] virtual Dom and as that that state [4:38] changes that virtual Dom is regenerated [4:41] each time and then reacts job is to [4:44] reconcile what came before with what [4:46] came after so let's see what this [4:49] process looks like first we look as a [4:51] top-level element it was a div is still [4:54] a div so we keep it then we need to look [4:55] at the attributes that had a class name [4:57] at but it still has a class name and so [4:58] we keep it then we look at the children [5:00] it had an h1 it still has an h1 so we [5:02] keep it then we look what's inside the [5:04] h1 some text it hasn't changed we keep [5:06] it look at the input this linen but we [5:08] keep it the value hasn't changed we keep [5:11] it look at a button it's still a button [5:12] we keep it this text has changed so we [5:15] need to apply that to the Dom all that [5:17] work to change a four to a five as [5:20] engine is we should be offended all that [5:22] inefficiency but that's not the bad part [5:23] the bad part is that we keep running [5:26] this code over and over again every time [5:29] there is any state change at all we need [5:30] to read Eclair these functions that [5:32] close over that local state the problem [5:35] here is that react doesn't have any [5:38] understanding of the values flowing [5:40] through your app it treats it as a black [5:41] box in other words by any reasonable [5:43] definition of the word react is not [5:46] reactive or as john lindquist said react [5:50] is a terrible name for react Jess [5:54] so is the virtual DOM fast no it's not [5:57] fast is the virtual DOM fast enough well [6:00] that's a more interesting question I [6:01] work with data visualization and [6:03] animation and I would say categorically [6:05] no it is not but you don't need to take [6:07] my word for it we can ask the react core [6:09] team the react core team don't think [6:10] that the virtual Dom is fast enough and [6:12] I know this because they've given us [6:14] things like should component update [6:15] react dot pure component use memo and [6:17] use callback these are all abstraction [6:19] leaks that let you the programmer tell [6:21] the computer that it doesn't need to [6:23] worry about this subtree within your [6:25] application in other words you're doing [6:27] the computers job for it [6:28] another sign that the virtual bomb isn't [6:30] fast enough is when we start to develop [6:32] amortization strategies like concurrent [6:34] mode which we'll talk about later so a [6:39] few years ago I was thinking what can we [6:40] do about this [6:41] I want my users to have a fast [6:43] application I want them to have a good [6:46] experience but I want to have the [6:47] experience of using a declarative [6:49] component framework like react and then [6:52] it occurred to me maybe we're thinking [6:53] about frameworks all wrong a framework [6:55] doesn't need to be a thing that runs in [6:58] the browser frameworks are not tools for [7:00] organizing your code there are tools for [7:02] organizing your mind so given that maybe [7:06] the framework should be a thing that [7:08] runs in your build step instead or as [7:10] Kyle was enthusiastically saying [7:12] yesterday compilers are the new [7:14] frameworks and so I started a project [7:17] called spelt and this is what it does it [7:20] takes your declarative components and [7:22] then it turns it into the the efficient [7:25] imperative low-level code that [7:27] manipulates the Dom directly the [7:30] interesting part here is changed how do [7:33] we figure out what changed now [7:35] frameworks have solved this problem in [7:37] different ways over the years [7:38] old-school react with classes had this [7:42] set state you pass it some new state it [7:44] merges it with the old state and it [7:46] triggers a rerender in modern react we [7:48] use hooks we call the second count [7:51] function and then it updates its [7:52] counterpart the count variable in [7:55] old-school spelt we had something very [7:58] similar to classical react this set [8:00] instead of this set state that also [8:03] triggers a rerender and that's [8:05] fine it works but the trouble is this [8:07] set State and this set they're kind of [8:10] verbose it's a lot of work to do just [8:12] play some state the thing that they have [8:14] in common the problem that they have in [8:15] common is that this as soon as your API [8:17] depends on of this you introduce all [8:20] sorts of constraints that are a little [8:21] bit difficult to anticipate at first and [8:24] so we wanted to improve upon that let's [8:26] take a step back how do we tell the [8:28] computer something changed well there's [8:31] actually a really good answer for this [8:32] already we use an operator specifically [8:34] we use the assignment operator we [8:37] declare a variable and then we say count [8:38] equals count plus one or more concisely [8:41] count plus equals one and to give credit [8:44] where it's due view figured this out a [8:46] long time ago in view you trigger [8:48] updates by assigning to properties this [8:50] dot count plus equals one now the way [8:54] that that works that run time is very [8:55] different it requires that you have of [8:57] this which has the problems that we were [8:59] trying to avoid so that won't work for [9:00] us those are just the rules of [9:02] JavaScript but we're a compiler we don't [9:03] need to play by everyone else's rules so [9:07] we are days away from the release of [9:09] version three as felt or if you're [9:11] watching this on YouTube hopefully has [9:12] already been released and spelled three [9:14] is different spelt three moves [9:16] reactivity out of the component API and [9:18] into the language and I'm gonna show you [9:20] what that looks like this is the same [9:22] application as before implemented in [9:24] spell up here let name equals world [9:27] instead of you state let count equals [9:29] zero and then we've got some similar [9:31] functions except this time we update [9:33] this count variable by assigning to it [9:36] count plus equals one and I can type in [9:38] here yeah like that click the button and [9:44] the application that pays accordingly [9:45] how does this work we go to the compiled [9:49] output and look at the code that the [9:52] compiler has generated we'll see that [9:54] it's actually left our code more or less [9:57] intact [9:58] it's just instrumented it with these [10:00] calls to the invalidate function every [10:03] time you assign to a variable that is [10:04] used inside your components markup it [10:06] calls invalidate invalidates job is to [10:08] say to the component this value has [10:10] changed be aware of that when you update [10:12] and by the way you need to update at the [10:14] end of the event loop [10:16] we can actually get a little bit more [10:18] efficient here we in spoke we don't need [10:21] to use event handlers just to sync [10:23] values between an input and a variable [10:26] we can use the bind directive get rid of [10:29] that and we can make this an inline [10:32] function and get rid of that and this [10:40] continues to work as before now an [10:45] interesting thing about doing it this [10:46] way putting JavaScript in our HTML [10:48] instead of putting HTML in our [10:50] JavaScript is that we can compile it to [10:52] different outputs depending on the needs [10:54] that we have so far we've been looking [10:56] at a JavaScript class that dates and [10:58] manipulates the Dom but we can also do [11:01] server-side rendering in SSL mode all [11:04] we're doing is concatenated a string and [11:07] this is orders of magnitude faster than [11:10] creating a component tree and then [11:12] serializing it what this means is that [11:14] if you do server-side rendering we [11:15] spelled your AWS bills will be lower [11:17] sell your product managers how you use [11:22] it it's just like any other JavaScript [11:23] module you import it using the import [11:26] keyword once your bundler is using the [11:28] spelt plug-in this gets converted on the [11:30] fly into a JavaScript class in the [11:32] client you just call new app and then on [11:34] the server you have a different API [11:36] because you're just serializing HTML it [11:39] call app bot render but we're not [11:42] reactive yet because in most components [11:46] you will have state that is derived from [11:48] other state just like in our spreadsheet [11:50] we have cells that are derived from [11:51] other cells and the way that this is [11:54] dealt with in react is well they don't [11:55] need to because the code runs over and [11:58] over again so if we have a list of to [11:59] do's here that can be done or not done [12:02] and if we toggle them by clicking on [12:03] them and if we can hide the ones that [12:05] we've done already then we can generate [12:08] that filtered list of hidden to dues or [12:10] unhidden to do's using Const filtered [12:13] and it will just regenerate that each [12:14] time and then down here at the bottom we [12:16] have a list maps over the filtered array [12:20] that's not ideal though because we might [12:24] have this component inside another [12:25] component which means that that filtered [12:27] variable keep getting regenerated [12:29] and necessarily it's dependences might [12:31] not have changed so in reacts what we do [12:34] is we wrap that in use memo and we pass [12:37] a second document which the dependencies [12:39] are filtered in our case to do's and [12:42] hide them and what use memo does is it [12:45] compares the variables in the second [12:46] argument with the values in the previous [12:49] run and if they're unchanged then it [12:50] knows that the return value is unchanged [12:52] and it just gives you back the cached [12:54] value and that's a bit more efficient [12:56] but it's obviously more work for the [12:57] developer is more boilerplate for us my [13:02] zoom out so that we can see the entire [13:04] in out component this is the spelt [13:06] equivalent it's a little bit leaner and [13:08] this is something that we see quite a [13:10] lot I've converted a lot of react [13:11] components to spell components and [13:13] typically what you find is the react [13:15] component is about 40% larger you have [13:16] to type 40% more code to get it to work [13:19] but the trouble here is that this [13:22] doesn't actually work right I'm clicking [13:24] on these these items but they're not [13:26] toggling and that's because this code [13:28] only runs once console turd [13:30] it's a con so it can't change we need to [13:32] find some way of telling spelt that this [13:35] is a reactive value that depends on [13:37] other reactive values and for that we [13:39] need to look for inspiration in the [13:40] outside world speaking of inspiration I [13:44] find this guy pretty inspirational Mike [13:45] Bostock is the creator of B 3 and is [13:47] also the creator of observable is anyone [13:49] familiar with observable here great this [13:52] is a hugely popular tool in the date of [13:54] his world it's an interactive notebook [13:56] generator it's similar to run kit or [13:58] Jupiter in the Python world things like [14:00] that he said it has a key difference [14:02] rather than running your notebook from [14:04] top to bottom it runs your code in [14:06] what's called topological order this is [14:08] a fancy way of saying that if statement [14:09] a depends on statement B statement B [14:11] will run first regardless of the order [14:13] in which they're declared in other words [14:15] it functions like a spreadsheet well we [14:18] like spreadsheets maybe this is an idea [14:19] that we could use in our components [14:22] there's a blog post I like by an [14:24] Australian developer called pulse devel [14:26] what is reactive programming and it [14:28] tells the story of some code [14:30] anthropologists from the year 2050 one [14:32] looking back and struggling to [14:33] understand the code that we wrote today [14:35] VAR a equals 10 VAR b equals a plus 1 [14:38] equals 11 well then we need to say 8 B [14:41] equals a plus 1 again [14:43] the relationship between those two [14:44] variables isn't fixed but in 2015 we [14:48] have the Destiny operator [14:49] Bobby destiny a plus one now if we [14:52] change the value of a the value of B [14:54] changes with it [14:55] they are bound it's explicit a part of a [14:57] language could we use the Destiny [15:00] operator in JavaScript well no we [15:02] couldn't it wouldn't be syntactically [15:03] valid and that would mean that we can [15:05] use parses we can use linters all of the [15:07] other things that are really valuable [15:08] but it turns out there is a piece of [15:10] syntax that we can use and there's a [15:12] piece of syntax that almost no one has [15:14] ever encountered let alone used does [15:16] anyone recognize this this is a thing [15:19] called a labelled statement and it [15:21] doesn't really do anything most of the [15:23] time in JavaScript so we on the spell [15:25] team have co-opted it we've made it our [15:27] own dollar b equals a plus 1 b is now [15:30] bound to the value of a plus 1 and the [15:33] guy kyle simpson was trying to warn you [15:34] about so we can add that to our original [15:41] component just go down here it's a Const [15:43] filtered and change it to dollar fill [15:45] third hey presto it's reactive that's [15:48] kind of handy and we can go further we [15:50] can say showing equals filtered length [15:53] and start to have reactive values that [15:57] depend on other reactive values showing [16:01] showing of two dues block length then [16:06] now if I start to toggle these it [16:09] updates automatically we're not limited [16:11] to declarations thank you [16:15] we're not limited to declarations we can [16:18] also have arbitrary statements in here [16:20] so we can do this kind of thing which is [16:22] very useful for debugging values as as [16:24] they change alright so how does this [16:26] work well if we look at the j/s output [16:28] from this and scroll down to where it's [16:32] placing the reactive values it's taken [16:39] these statements and it's wrapped them [16:41] in if statements if change I'd done or [16:44] change to dues then we recalculate [16:46] filtered and we invalidate filtered and [16:49] you'll notice that because it's figured [16:51] out that showing depends on filtered it [16:54] has to calculate filtered before it [16:56] calculates showing that's the [16:58] topological ordering that Mike Bostock [17:00] was talking about and this is all [17:03] extremely fast for the browser to run [17:06] this is very efficient code now a lot of [17:09] people when they see this for the first [17:10] time they're like I'm not sure about [17:12] that that's a little bit magical and [17:15] they don't mean magical in the good way [17:17] they mean the bad kind of magic ever no [17:19] that creates the review j/s said well [17:21] this isn't even JavaScript this is [17:23] technically spell script and he's right [17:25] we are abusing the language for fun and [17:27] profit but I find it a little bit odd [17:28] that 'even of all people is the one [17:30] saying this when he perhaps more than [17:32] anyone else more even than michele has [17:34] popularized the idea of assignments [17:37] having side effects in JavaScript the [17:39] distinction that he draws is between [17:41] magic that happens in the browser and [17:43] magic that happens at compile time I [17:45] think this is an arbitrary distinction I [17:47] think if you draw a line between those [17:48] two things then you're guilty of making [17:50] the same mistake as people who used to [17:52] say that templates and styles and [17:54] JavaScript should be in separate files [17:56] even though they relate to the same [17:57] component what matters is the [17:59] functionality not where the [18:01] functionality happens [18:04] something I like to do when framework [18:08] authors release benchmarks showing how [18:10] fast their framework is I had spelt true [18:12] and then I tweaked out the result it is [18:15] probably why I don't have many friends [18:16] but it's so worth it [18:18] because look this is an old one and it's [18:20] showing the spelt is 35 times faster [18:23] than react and 50 times faster than view [18:26] Dan on the react team thinks that this [18:29] benchmark is meaningless and I kind of [18:30] agree with him there is no point [18:32] updating your application hundreds of [18:34] thousands of times a second if your [18:35] refresh rate this 60 Hertz Dan thinks [18:38] that we should be measuring user [18:40] experience instead and I agree but he [18:43] says that we should be but he doesn't [18:46] say that we should be but Dan is talking [18:49] specifically about something called [18:50] concurrent mode which is a feature that [18:51] has come in to react soon and we need to [18:54] talk about concurrent mode and the best [18:56] way to do that is to use one of Dan's [18:58] demos from a previous conference talk [19:00] many of you probably seen this this is [19:02] from Jes confit [19:03] Iceland if you haven't seen this talk I [19:04] I really do urge you to do so it's an [19:06] incredible talk that really changed a [19:08] lot of people's understanding about how [19:10] this stuff works so if we start typing [19:13] into the same pot this is react today [19:20] now maybe you can't see what's going on [19:24] here because you're in the audience [19:25] you're not typing but for me this is a [19:27] very frustrating experience because my [19:29] keys aren't having an immediate effect I [19:31] can't see the input taking place so if [19:35] we fire at the frustration meter and try [19:38] it again [19:48] right that's not that's no fun in a way [19:51] that we have often tried to fix this is [19:53] using a technique called debouncing I [19:59] can't type [20:06] right with the bouncing nothing happens [20:08] it's like stop typing and then [20:10] everything updates all in one go and it [20:12] means that the main thread stays [20:14] unblocked and the apps there is [20:15] responsive but we don't get any feedback [20:17] so that's not really a great user [20:19] experience now [20:21] concurrent modal async mode as it was [20:23] referred to at the time is the best of [20:25] both worlds yeah updates while I type [20:29] but if it gets too much to be too much [20:31] then it will chunk up the work and yield [20:35] back to the main thread so the [20:40] frustration meter stays green the whole [20:41] time but you might notice that as we get [20:45] more and more complex it starts to get a [20:48] little bit slow to update the graph in [20:50] the background this is the same [20:52] application implemented in spell [21:05] it's a very different user experience I [21:07] don't need to tell you whether that's [21:09] better than the time slicing one it's [21:10] obvious what's better and even in the [21:12] frustration meter I mean you're welcome [21:19] to you'll probably get a better result [21:23] than you do with react but inferno is [21:26] still a virtual DOM mechanism and it's [21:29] gonna have some of the same fundamental [21:31] bottlenecks I mean you're welcome to try [21:36] and if you do it then I will add spell [21:40] and I will tweet it out and we'll we'll [21:41] compare notes so just to show you one [21:45] more time in async mode with with react [21:47] and it's nice at first but it quickly [21:51] gets a bit chunky now it's obviously [21:55] better than react without time slicing [21:58] but it's not clear to me that it's [22:00] better than just having a framework that [22:03] doesn't have these bottlenecks in the [22:04] first place the best way to deliver a [22:07] good user experience is to be extremely [22:09] fast [22:15] an analogy that I like to use is that of [22:18] the internal combustion engine the [22:19] internal combustion engine is a marvel [22:22] of engineering modern ICS are incredible [22:25] devices car companies have spent [22:28] millions of dollars eking out [22:30] incremental gains over the years but [22:33] then Tesla came along and said well what [22:35] if we just changed the underlying [22:36] assumptions what if we don't use fossil [22:38] fuels at all well then we have a sit at [22:41] different set of constraints the average [22:43] internal combustion engine has hundreds [22:45] and hundreds of moving parts [22:46] the electric motor inside the Tesla has [22:48] two an internal combustion engine the [22:51] best one in the world has an efficiency [22:53] of maybe twenty twenty-five percent the [22:56] typical electric motor has an efficiency [22:58] of 85% so car companies are spending all [23:01] this money trying to eke out these [23:03] incremental gains from the internal [23:06] combustion engine when actually the game [23:07] has changed so a few different [23:11] strategies for speeding up our code [23:13] breaking it up into small chunks and [23:16] moving it around like time slicing it [23:17] doesn't really speed up your code it [23:19] might make it feel faster but it doesn't [23:21] fundamentally change what's happening a [23:23] few years ago everyone was trying to put [23:25] their code in webworkers to unlock the [23:27] main threat that way but no one does [23:28] that anymore because it turns out it [23:29] just doesn't work you can't move the [23:31] code around into a different place and [23:32] expect it to have a meaningfully [23:34] different result [23:35] you can't even rewrite it in rust it [23:38] turns out last month a guy called Nick [23:41] Fitzgerald wrote a blog post about a [23:44] library that he's working on at the [23:46] moment called Dodge Rio this is a [23:47] virtual Tom implementation written in [23:49] rust and compiled to wasm which is you [23:51] know pretty mind-blowing and the blog [23:53] post boasted of best-in-class [23:55] performance [23:56] now when you say best-in-class [23:57] performance it's like if you stand in [23:59] front of the mirror and say Candyman [24:01] five times I will appear behind you so I [24:04] added spell 2 to this benchmark and sure [24:07] enough spelt house better than [24:09] best-in-class performance actually the [24:12] benchmark that it was using it was using [24:14] set timeout for the scheduling which [24:16] meant that most of the benchmark was [24:17] just waiting for the browser's timer to [24:19] run so I changed the implementation to [24:21] use promise resolution instead which [24:22] gives us a better idea of the raw Dom [24:24] update performance and we can see on the [24:26] right that [24:28] beats da geo by a larger margin now I am [24:31] bullish on rust and wasm in general I [24:34] think there are a lot of good reasons [24:35] why you would want to use rust for [24:36] different parts of your application and [24:38] I think that Nick has done some really [24:39] incredible work here but the virtual Tom [24:41] mechanism is fundamentally not going to [24:44] yield the best performance there's only [24:48] one reliable way to speed up your code [24:50] and that is to get rid of it be like [24:53] Mary Kondo does this javascript spark [24:55] Troy now when people like me talk about [25:00] performance who are often accused of [25:02] having our heads in the clouds they're [25:03] not being concerned with the real-world [25:06] impacts of our applications and I find [25:09] that so confusing performance is the [25:11] most real-world thing there is stone is [25:15] a company in Brazil that makes points of [25:16] sale systems that you put your credit [25:18] card into and they tried building the [25:20] interface for these devices with react [25:22] and with view and with a panoply of [25:24] other frameworks and they couldn't get [25:26] the results that they wanted it was just [25:28] to slow the user experience was awful [25:30] they built it with felt instead and it [25:33] worked really well so now if you want to [25:35] talk about real world there are 200,000 [25:37] these devices in the real world on the [25:39] streets of Brazil running spelt these [25:42] are low powered devices that don't have [25:44] the same amount of memory and CPU [25:47] capacity that we have on a mobile device [25:49] normally most lab in Russia is another [25:52] example of a company that gets this they [25:53] make applications for smart TVs and [25:56] things like that and for them spell is [25:58] the perfect fit it allows them to be [25:59] extremely productive without having to [26:01] worry about performance and they're [26:04] expanding svelte now into other areas of [26:06] their business including a control panel [26:08] for smart home automation this is a [26:11] trend that is only going to increase the [26:13] mobile web is not the new frontier [26:15] anymore the new frontier is the embedded [26:17] web wearables Internet of Things Smart [26:21] TVs in-car entertainment all that kind [26:24] of stuff and the traditional JavaScript [26:26] first framework simply will not cut the [26:28] mustard we need something more powerful [26:32] but I'm done talking about performance I [26:34] want to talk about some of the other [26:35] benefits that svelt can give you first [26:38] that accessibility [26:42] this is one of the things that [26:44] developers all care about in theory but [26:47] something that we habitually get wrong [26:49] because it's kind of difficult like a [26:50] lot of people working in development we [26:52] don't have visual impairments very often [26:54] often we have you know good motor skills [26:57] and so on and so it's difficult for us [27:00] to appreciate how difficult using the [27:02] web can be for some people and I think [27:04] that tools can really help us solve this [27:06] problem if you write marker that is [27:09] inaccessible spelt will yell at you here [27:12] we've got an image tag with a source [27:13] attribute but it doesn't have an alt [27:15] attribute which means that someone using [27:17] a screen reader wouldn't know what was [27:18] in this image or someone even on a slow [27:21] connection who couldn't download the [27:22] image in the first place and so down [27:25] here I don't if you can see it at the [27:26] back there is a warning is saying [27:28] accessibility image element should have [27:30] an alt attribute and it's pointing to [27:32] where the image is this is a warning [27:33] that you would get in your terminal if [27:34] you're using this in a conventional [27:36] build setup so we can add an alt tag and [27:41] the warning goes away it's still [27:43] compiled but we got a warning it will [27:46] let you write market that isn't [27:48] accessible but it won't respect you for [27:50] it [27:52] who works on a web application that [27:55] doesn't have any styling no one okay [27:59] good [27:59] so if styling is such a fundamental [28:01] parts of the web that we build why do we [28:04] use frameworks that don't deal with [28:06] styles their unfinished spell on the [28:08] other hand because we're basically [28:10] writing into a superset of HTML we can [28:12] use style tags right inside our [28:13] components we can have a paragraph tag [28:15] that you that has a style from this P [28:19] selector here we haven't had to use [28:20] classes we haven't had to use complex [28:24] and namespacing schemes like man we're [28:26] just using the paragraph selector and [28:29] yet are nested component here even [28:32] though it has a paragraph is unaffected [28:34] by the styles in the parent component [28:35] and I can add some styles to this one as [28:38] well P text transform uppercase right [28:46] and it only affects the child component [28:47] not the top one the way that this works [28:50] is it adds a computer-generated unique [28:54] hash to the classes and then it applies [28:56] that to both the markup and the styles [28:58] another cool thing that we can do if a [29:00] framework understands both markup and [29:02] styles is it can tell us when our [29:08] declarations are not being used by the [29:10] markup and it will actually just remove [29:12] it you can see that that style there [29:14] isn't reflected in the CSS output that [29:17] it gets written to disk but it also [29:19] gives us a warning so that we can remove [29:21] that this solves the problem that so [29:23] many of us have experienced where you're [29:25] terrified of removing any of the styles [29:27] from your style sheets because you don't [29:29] know what they affect right we've all [29:32] experienced this at some point [29:33] CSS nsj is designed to solve this [29:35] problem and it does so pretty well you [29:37] can use CSS and J's misspelled but you [29:39] probably won't need to because it has [29:40] file handling built into the framework [29:46] something else that we can do with spelt [29:48] is add transitions if I toggle this [29:52] visible boolean which is literally just [29:56] that then the element doesn't disappear [30:01] and reappear and doesn't a snap into [30:03] place instead it transitions out [30:05] gracefully this is really good for like [30:06] notifications and things like that and [30:09] the way that that is done is just by [30:12] adding this transition fly directive to [30:16] the element and then giving it some some [30:19] arguments there's an object literal with [30:21] with one property twice and that's just [30:23] imported from this svelte transition [30:25] package here now if I toggle this really [30:28] quickly you can see that it actually [30:29] reverses the transition smoothly and [30:31] puts it back into the place where it's [30:33] supposed to be which is kind of [30:34] difficult to do with with some [30:36] frameworks interestingly though this [30:39] isn't done with JavaScript when the [30:41] state changes spell will calculate a CSS [30:45] animation and then it will apply that to [30:47] the element because CSS animations run [30:49] off the main thread this is more [30:50] performant and more battery friendly [30:52] particularly on mobile devices than if [30:54] you're doing the animation inside [30:55] JavaScript and we can get pretty funky [30:58] with this stuff we can create our own [30:59] custom transitions this is a a spin [31:02] function which takes a node and it takes [31:04] some arguments optionally and then it [31:06] returns an object with a CSS method [31:09] which takes T a value between 0 and 1 0 [31:13] is fully off the screen one is fully on [31:15] the screen and it returns some CSS so [31:19] this kind of like JavaScript in CSS and [31:21] then smoke will take that compile it to [31:23] a CSS animation and then it will play it [31:27] on the element so let's import spin from [31:30] spin s and then down here we'll replace [31:35] that with in spin and out fade and now [31:39] when we play it end you can go crazy and [31:44] we can use easing functions that aren't [31:45] available in CSS normally all that kind [31:47] of stuff and yet it's all done [31:49] completely in CSS in a nice performant [31:51] way and we can get pretty complex with [31:54] that stuff I'm not going to show you all [31:56] the code for this example but I do want [31:58] to show you [31:59] conceptually what's happening we've got [32:01] a to-do list with two views of it we've [32:04] got one that is the two dues filter by [32:08] the ones that have not been done and [32:12] then beneath it we've got one that's [32:13] filtered with all of the dues that [32:15] happened then and as we toggle these [32:17] they move from one list to the other but [32:22] that's quite unsatisfying because that's [32:24] not how objects behave in the real world [32:26] our brains don't don't like it when [32:28] things just disappear and reappear [32:29] elsewhere on the screen what we run is [32:32] for these things to move gracefully to [32:34] their new position that helps us [32:36] understand what's actually happening to [32:37] the things inside the application and so [32:41] I'm going to take this label here and [32:42] I'm gonna add some directors to it and [32:46] now when I toggle these they move out of [32:50] the way cleanly like that and again this [32:53] is all done with CSS and not JavaScript [32:57] this is the kind of feature that most [33:00] frameworks don't include and the reason [33:03] for that is because in the JavaScript [33:04] world with very conscious of bundle size [33:07] often you go to a frame rate marketing [33:10] page and they will have the size of the [33:11] framework right there on the front of [33:13] the page saying look with a three [33:15] kilobyte alternative to react in finance [33:17] case and that's good because it's better [33:21] if we can deliver less JavaScript to our [33:23] users but it means that we're [33:24] restricting the number of features that [33:26] we can include in our frameworks but [33:29] spelt is a compiler it doesn't have the [33:31] same constraints if you're not using a [33:33] particular feature like transitions or [33:35] bindings then we don't need to include [33:37] the code that makes that feature [33:38] possible so you're always going to get [33:40] the minimal subset of the frameworks [33:42] features it means that we've decoupled [33:45] feature set from file size and that lets [33:47] us the spelt team be far more expansive [33:50] and the kinds of features that we [33:51] consider worth including in the [33:53] framework and that's good for developers [33:55] because developers has developers we [33:57] want all the features but we just don't [33:59] want to pay for the ones that we're not [34:00] using and what's good for developers is [34:03] ultimately good for users [34:06] so spell is a low-level component [34:09] framework and you can use it to build [34:10] pretty complex applications but at some [34:12] point you will need something a little [34:14] bit more more well-rounded and so we [34:17] have a project called sapper is a [34:19] companion project just felt if you're [34:21] familiar we've react and you've used [34:22] Next and Gatsby those are frameworks [34:24] that take the component framework and [34:26] turn it into an application framework so [34:28] that you get routing and code splitting [34:31] server-side rendering and all the [34:33] features that make a modern web [34:35] application easy to develop but [34:37] performance sapper is like that except [34:39] with much less JavaScript because it's [34:41] spelled you get automatic server-side [34:42] rendering you get automatic code [34:44] splitting all of that good stuff there's [34:46] a community project called spelt native [34:48] which will probably become an official [34:51] project at some point but it's still [34:52] quite new it's a community driven [34:54] project based on native script view and [34:56] it allows you to build iOS and Android [34:58] applications using spelt and beyond that [35:01] something that I really want to work on [35:02] is spelt GL which would be like 3j s but [35:06] spelter you would use the same kind of [35:09] declarative style to describe a [35:11] three-dimensional scene graph and then [35:12] it would get compiled to the low like [35:14] the low-level underlying draw calls that [35:17] would make that possible without all of [35:19] the the added bloat of a conventional [35:21] web GL library so in conclusion [35:26] spreadsheets are pretty cool and we [35:28] should be more like them there's [35:30] actually one more thing I want to say [35:31] about spreadsheets and that is how [35:33] accessible they are my wife is not a [35:35] programmer and after being with me for [35:37] 15 years she does not want to become a [35:39] programmer but she can do some [35:40] incredible things with spreadsheets when [35:42] I look over the shoulder when she's [35:43] working my mind is blown at the amount [35:46] of value contained in businesses around [35:48] the world in these humble spreadsheets [35:50] wouldn't it be wonderful if the tools [35:53] that we used to build the web were as [35:55] accessible the spreadsheets are and that [35:57] is one of spelts overriding goals is to [36:00] make web development accessible without [36:03] sacrificing power for power users my [36:06] friend Eliza working at Spotify and was [36:10] using spell to work on a project and her [36:12] project manager was able to come over [36:14] and work with them manipulate the [36:15] component code directly in a way that [36:17] would have been more intimidating [36:19] it was a javascript file when all [36:22] different people with different [36:24] disciplines and different skill sets can [36:26] crowd around the same laptop and work on [36:28] the same piece of code simultaneously [36:30] that's when great things happen so I [36:33] hope that one day we'll be able to look [36:34] back it's felt and say that in a strange [36:36] way it brought us all a little closer [36:39] together thank you so much for listening