What's Coming in Vue 3.0
Evan You

"How many of you have used Vue?" Most of the audience's hands go up

Started development on Vue 3.0 last year. Going slow, not trying to rush out a new product, want to make sure it's good decisions. We take stability quite seriously.

Design goals

Making it fast

Original plan was changing internal reactivity from Object.defineProperty (ES5) to Proxy (ES6). Proxy allows us to create an obseravtion system that's more efficient and covers the full language features.

Also rewriting virtual DOM rewrite.

More compile-time optimizations (slots compiled as functions by default, monomorphic vnode factory, compiled-generated flags for vnode/children types

At the end of last year, 3.0 prototype was already twice as fast (284.5ms from 2.5 to 126ms in 3.0-proto-2018-11)

But it's not good enough.

We saw interesting strategies pushed out by other frameworks/libraries. Like React hooks...marginally faster doesn't do enough.

What is the real bottleneck in Vue's performance? Bottleneck of traditional Virtual DOM is compiled into render functions, virtual DOM feature, differences patch the dom. "Vue is fast because of virtual DOM", but it's actually a liability since you're creating objects on and such. While Vue can ensure minimal updates at the component tree level, it's still a full diff inside each component instance.

The performance of traditional VDOM is determined by the total size of the template rathe than the amount of dynamic content in it.

  <div id="content">
    <p class="text">Foo</p>
    <p class="text">Foo</p>
    <p class="text">{{ message }}</p>
    <p class="text">Foo</p>
    <p class="text">Foo</p>

Diff <div>, diff props of div, diff children of div, diff p, diff props of p, diff children of p...

If you have 10000 nodes with only one thing that can change, it'd diff all 10000 instead of the one thing that changes

when you skip templates and just use render functions, extremely difficult for compiler to tell what to do. Fully dynamic render logic makes it very difficult to make safe assumptions about the user's intent

Syntax constraints does also lead to better optimizability

Example: Svelte. <h1>Hello {name}!</h1> -> p(changed, ctx) { if (changed.name) { set_data(t1, ctx.name); } } That's the whole update function, no unnecessary work.

Why not ditch Virtual DOM?

Full expressiveness of JavaScript, more succinct generated code. Backwards compatibility!

Who uses Vuetify? (a few hands go up)

It's not about rendering, it's about handling complex user input

When you look at Svelte code/compiler, it does do more code

Given we can't get rid of Virtual DOM, what do we do?

What's unique about Vue?

Virtual DOM as a lower-level rendering implementation. Template as higher-level syntax with lots of useful information for optimizations.

What should Vue do?

Provide the ability to drop down to render functions when needed, but maximize time outside

The new template compilation strategy in 3.0

Start with the simplest case when node structure changes at all, and only a single dynamic change

<p v-if="ok">
  <p>{{ message }}</p>

Imagine if we were compiling it, but how about we just record the list of dynamic nodes we detect. What if we have node structure changes (like v-if or v-for)? Break code into two parts: outside v-if (v-if is the only dynamic node) and inside v-if (message is the only dynamic node)

...it's a block tree! Template divided into "blocks" based on structural directives.

With the new strategry, update performance is determined by the amount of dynamic content instead of total template size. If you have huge static content in the vue template, they won't pay update costs (ignored).


v-for with 1000 iterations, 12 DOM elements nested 3 levels deep, 2 dynamic class bindings, 1 dynamic text interp, 1 dynamic id attribute binding

Vue 2.6.10: 36ms per update. 3.0: 5.44ms (more than 6x faster in this benchmark)

tl;dr vue 3.0 faster


Some of the pain points we've seen in large-scale projects: non-ideal typescript support, massive components that are hard to maintain (mega-component), lack of a clean and cost-free logic reuse pattern.

We opened a Class API RFC, but cancelled.

Why drop the Class API?

It was originally designed for better TS support, exposing props and other injected properties on this is still problematic. Decorators TC39 proposal is highly unstable and risky to rely on.

Turns out it offers nothing else (except for familiarity)

So yeah, let's drop it. Someone inspired by React hooks, though...

Function-based API

Imagine we're building Vue with lower-level APIs. Reactively apply side-effects (e.g. watching and rendering something)

If you do state.count++, updates the dom. How do we handle user input?

import { reactive, watch } from 'vue'

const state = reactive({ count: 0 })

function increment() { state.count++ }

const renderContext = {state, increment}

watch(() => {})

Computed values

const double = computed(() => state.count * 2);
console.log(double.value) // 0

console.log(double.value) // 2

If we put it all in a component, we have a setup() with a state const, reactive, increment function

Organizing code by options vs by feature

Code related to a feature is split between different option blocks currently. Now, return bindings in a template, combine it by feature instead of by whatever block it happens to be.

Reusing logic between components...

Better type inference

RFC: github.com/vuejs/rfcs/pulls/42

Once that's RFC's done, expect first alpha soon afterwards