1 of 61

Vite & VitePress

Evan You

VueConf Toronto 2020

2 of 61

What is Vite?

3 of 61

Vite

[veet]

French: rapid, quickly

4 of 61

Vite

Functionally similar to

pre-configured

webpack + webpack-dev-server

5 of 61

What makes it special?

6 of 61

It’s fast. Like, really fast.

  • Server starts in <300ms
  • Hot Module Replacement (HMR) updates in <100ms
  • Esbuild-powered TS/JSX transforms
  • On-demand: only process modules of current code-split route

7 of 61

New Vue 3 project / 10 components / no Babel / 2nd run

�Dev server start time

vue-cli 2568ms�vite 232ms

Page load time

vue-cli 320ms�vite 379ms

Build time

vue-cli 5.14s�vite 2.39s

8 of 61

Native-ES-modules-based

server for development

+

Rollup-based build for production

9 of 61

Native-ES-modules-based

server for development

The Why

10 of 61

The Why

  • Traditional “bundlers” require a full build of the entire application before the dev server can display anything

11 of 61

The Why

  • Traditional “bundlers” require a full build of the entire application before the dev server can display anything
    • This includes full-blown parsing of every single file for their import/export relationships!

12 of 61

The Why

  • Traditional “bundlers” require a full build of the entire application before the dev server can display anything
    • This includes full-blown parsing of every single file for their import/export relationships!
    • Algorithms to sort, rewrite, and concatenate all the modules together

13 of 61

The Why

  • The bigger your app gets, the slower your dev server starts up
  • Code splitting helps prod perf, but does NOT help dev perf

14 of 61

Bundle based dev server

entry

route

route

module

module

module

module

...

...

15 of 61

Bundle based dev server

entry

route

route

module

module

module

module

...

...

Dynamic import

(code split point)

16 of 61

Bundle based dev server

entry

route

route

module

module

module

module

...

...

Bundle

17 of 61

Bundle based dev server

entry

route

route

module

module

module

module

...

...

Bundle

Server

ready

18 of 61

Native-ES-modules-based server for development:

How Vite does it

  • <script type="module">
  • Browser natively parses and resolves ES module imports
  • The dev server receives HTTP requests for individual modules

19 of 61

Native ESM based dev server

Server

ready

20 of 61

Native ESM based dev server

Server

ready

HTTP request

entry

route

route

module

module

module

module

...

...

21 of 61

Native ESM based dev server

Server

ready

HTTP request

entry

route

route

module

module

module

module

...

...

Dynamic import

(code split point)

22 of 61

Problem: HTTP request waterfall

  • Page loads slower vs. bundled when module count is large
  • Prominent for dependencies with many internal modules, e.g. lodash-es

23 of 61

Problem: HTTP request waterfall

  • Optimization 1: pre-bundling dependencies
    • Ensure 1 file / 1 request per dep
    • Heuristics to determine bundled deps:
      • ESM entry w/ multiple imports
      • CommonJS

24 of 61

Problem: HTTP request waterfall

  • Optimization 2: etag & 304 Not Modified headers

25 of 61

Problem: HTTP request waterfall

  • Optimization 3: code split
    • Native ESM means code splitting improves both dev and prod performance!

26 of 61

Problem: Native ESM doesn’t support bare imports (yet)

import { createApp } from 'vue'

27 of 61

Problem: Native ESM doesn’t support bare imports (yet)

  • Import maps addresses this, but it’s not standardized yet
  • What can we do now?

28 of 61

Problem: Native ESM doesn’t support bare imports (yet)

  • Lightweight module rewriting with es-module-lexer + magic-string
  • No full AST parse/transforms, extremely fast (<1ms for most files)

29 of 61

Problem: Native ESM doesn’t support bare imports (yet)

Source�

import { createApp } from 'vue'

Rewritten�

import { createApp } from '/@modules/vue'

30 of 61

Challenge: HMR over native ESM

  • Limitation: no actual way to “swap” a native ESM module

31 of 61

Challenge: HMR over native ESM

1. Record module import graph while rewriting imports

entry

module

module

module

module

module

module

...

...

Record import chains

32 of 61

Challenge: HMR over native ESM

2. import.meta.hot usage marks file as “HMR boundary”

entry

boundary

module

module

module

module

module

...

...

import.meta.hot.accept(...)

33 of 61

Challenge: HMR over native ESM

3. When a file changes, we trace its importer chains to look for the HMR boundaries

entry

boundary

module

module

module

changed

module

...

...

34 of 61

Challenge: HMR over native ESM

�4. Boundaries re-import the changed module and apply updates

entry

boundary

module

module

module

changed

module

...

...

HTTP request

35 of 61

Challenge: HMR over native ESM

�5. If any of the parent chains reach a “dead end”, a full reload will be triggered to ensure consistency.

entry

boundary

module

module

module

module

changed

...

...

Reached root without

HMR boundary

36 of 61

Rollup-based build for production��The Why

  • Rollup is the best-performing JS-based bundler in terms of build speed, tree-shaking and output size
  • It’s also built around ES modules, which aligns with Vite’s premise

37 of 61

Rollup-based build for production��The How

  • Rollup itself is relatively less often used for bundling apps due to the difficulty of configuring it to handle app-specific use cases (e.g. CSS)
  • Vite ships with an opinionated Rollup configuration that works out of the box for most common cases.

38 of 61

Rollup-based build for production��The How

  • A decent number of module transform logic can be reused
  • Vite also provides an abstracted Transform API that works for both dev and prod

39 of 61

Vite get started in a minute�

npm init vite-app

40 of 61

Vite is extensible

Check out community plugins at awesome-vite

41 of 61

Vite is also framework-agnostic�

npm init vite-app --template react

See all templates at create-vite-app

42 of 61

Other native-ESM-based dev servers:

snowpack / es-dev-server

And their differences from Vite

43 of 61

VitePress

Static-site generator built on top of

Vite + Vue 3

[Still experimental]

44 of 61

VitePress

Like VuePress, but faster, lighter and more minimalistic

45 of 61

Drawbacks of VuePress

  • Slow server start
  • Slow edit feedback (HMR)
  • Double payload

46 of 61

Drawbacks of VuePress

  • Slow server start
  • Slow edit feedback (HMR)
  • Double payload

Solved by Vite!

47 of 61

The double payload problem

48 of 61

Single-Page Application

Static Site Generator

(SPA-SSG)

49 of 61

SPA-SSG

  • Pre-rendered static generation for fast initial page load
  • Turns into an SPA after load for snappy subsequent navigation
  • DX and UX of an SPA
  • SEO and loading perf of a static site

50 of 61

Dynamic Markdown

  • Markdown is actually compiled to Vue template
  • Vue components can be used inside Markdown
  • Easily embed dynamic parts inside static content
  • MDX is a similar solution for React

51 of 61

Cost of Dynamic Markdown

“Double payload” + unnecessary hydration

HTML Payload

JS Payload

static

dynamic

static

Compiled

Render function

hydrate

hydrate

hydrate

52 of 61

Dealing with double payload

Leverages Vue 3’s compiler optimizations for

better production performance

53 of 61

Vue 3 static tree hoisting

54 of 61

Vue 3 static tree hoisting

  • Improves both update and hydration efficiency

HTML Payload

JS Payload

static

dynamic

static

Compiled

Render function

skip

hydrate

skip

55 of 61

VitePress static string removal

Before

const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<div>static content...", 1)

After

const _hoisted_1 = /*#__PURE__*/_createStaticVNode("", 1)

56 of 61

VitePress static string removal

  • Page JS payload only contains code for dynamic parts

HTML Payload

JS Payload

static

dynamic

static

Render fn

skip

hydrate

skip

57 of 61

VitePress static string removal

vue-router@next docs page

Vue 3 + Vite SPA + Default theme = 25.8kB

Page-specific JS = 639B!

58 of 61

SPA Navigation

  • Full JS including static content still needed for SPA navigation
  • VitePress produces two versions of each page’s JS
    • page.md.lean.js� -> static content removed, only for initial page load
    • page.md.js� -> full payload for SPA navigation

59 of 61

Bundle size optimization

  • Due to code splitting + filename hashing, hash data for all pages must be included in each individual page
  • In VuePress, each additional page bloats the bundle of every page by ~200 bytes
  • VitePress uses a custom routing solution that adds only ~20 bytes per additional page.

60 of 61

More optimizations to come...

  • Built-in viewport based pre-fetch
  • Built-in service worker
  • Vue core feature to allow marking part of component template only dynamic during ssr

61 of 61