Skip to content

Commit 0692528

Browse files
committed
perf
1 parent 62960ed commit 0692528

File tree

4 files changed

+149
-21
lines changed

4 files changed

+149
-21
lines changed

src/guide/best-practices/performance.md

Lines changed: 141 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,163 @@
22
aside: deep
33
---
44

5-
# Performance Guide <Badge text="WIP" />
5+
# Performance
66

7-
## Page Load Performance
7+
## Overview
8+
9+
Vue is designed to be performant for most common use cases without much need for manual optimizations. However, there are always challenging scenarios where extra fine-tuning is needed. In this section, we will discuss what you should pay attention to when it comes to performance in a Vue application.
10+
11+
First, let's discuss the two major aspects of web performance:
12+
13+
- **Page Load Performance**: how fast the application shows content and becomes interactive on the initial visit. This is usually measured using web vital metrics like [Largest Contentful Paint (LCP)](https://web.dev/lcp/) and [First Input Delay](https://web.dev/fid/).
14+
15+
- **Update Performance**: how fast the application updates in response to user input. For example, how fast a list updates when the user types in a search box, or how fast the page switches when the user clicks a navigation link in a Single-Page Application (SPA).
16+
17+
While it would be ideal to maximize both, different frontend architectures tend to affect how easy it is to attain desired performance in these aspects. In addition, the type of application you are building greatly influences what you should prioritize in terms of performance. Therefore, the first step of ensuring optimal performance is picking the right architecture for the type of application you are building:
18+
19+
- Consult [Ways of Using Vue](/guide/extras/ways-of-using-vue.html) to see how you can leverage Vue in different ways.
20+
21+
- Jason Miller discusses the types of web applications and their respective ideal implementation / delivery in [Application Holotypes](https://jasonformat.com/application-holotypes/).
22+
23+
## Profiling Options
24+
25+
To improve performance, we need to first know how to measure it. There are a number of great tools that can help in this regard:
26+
27+
For profiling load performance of production deployments:
28+
29+
- [PageSpeed Insights](https://pagespeed.web.dev/)
30+
- [WebPageTest](https://www.webpagetest.org/)
31+
32+
For profiling performance during local development:
33+
34+
- [Chrome DevTools Performance Panel](https://developer.chrome.com/docs/devtools/evaluate-performance/)
35+
- [`app.config.performance`](/api/application.html#app-config-performance) enables Vue-specific performance markers in Chrome DevTools' performance timeline.
36+
- [Vue DevTools Extension](/guide/scaling-up/tooling.html#browser-devtools) also provides a performance profiling feature.
37+
38+
## Page Load Optimizations
839

940
### Bundle Size and Tree-shaking
1041

11-
### Full Build vs. Runtime-only build
42+
One of the most effective ways to improve page load performance is shipping smaller JavaScript bundles. Here are a few ways to reduce bundle size when using Vue:
43+
44+
- Use a build step if possible.
45+
46+
- Many of Vue's APIs are ["tree-shakable"](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) if bundled via a modern build tool. For example, if you don't use the built-in `<Transition>` component, it won't be included in the final production bundle. Tree-shaking can also remove other unused modules in your source code.
47+
48+
- When using a build step, templates are pre-compiled so we don't need to ship the Vue compiler to the browser. This saves **14kb** min+gzipped JavaScript and avoids the runtime compilation cost.
49+
50+
- If you are using Vue primarily for progressive enhancement and prefer to avoid a build step, consider using [petite-vue](https://github.com/vuejs/petite-vue) (only **6kb**) instead.
1251

1352
### Code Splitting
1453

15-
### Big List Virtualization
54+
Code splitting stands for the build tool splitting the application bundle into multiple smaller chunks which can then be loaded on demand or in parallel. With proper code splitting, features required at page load can be downloaded immediately, with additional chunks being lazy loaded only when needed, thus improving performance.
1655

17-
### Avoid Unnecessary Component Abstractions
56+
Bundlers like Rollup (which Vite is based upon) or webpack can automatically create split chunks by detecting the ESM dynamic import syntax:
57+
58+
```js
59+
// lazy.js and its dependencies will be split into a separate chunk
60+
// and only loaded when `loadLazy()` is called.
61+
function loadLazy() {
62+
return import('./lazy.js')
63+
}
64+
```
65+
66+
Lazy loading is best used on features that are not immediately needed after initial page load. In Vue applications, this is typically used in combination with Vue's [Async Component](/guide/components/async.html) feature to create split chunks for component trees:
67+
68+
```js
69+
import { defineAsyncComponent } from 'vue'
70+
71+
// a separate chunk is created for Foo.vue and its dependencies.
72+
// it is only fetched on demand when the async component is
73+
// rendered on the page.
74+
const Foo = defineAsyncComponent(() => import('./Foo.vue'))
75+
```
1876

19-
## Update Performance
77+
If using client-side routing via Vue Router, it is strongly recommended to use async components as route components. See [Lazy Loading Routes](https://next.router.vuejs.org/guide/advanced/lazy-loading.html) for more details.
2078

21-
### Reducing Reactivity Overhead
79+
### SSR / SSG
2280

23-
- Use shallow APIs to hold immutable data
81+
Pure client-side rendering suffers from slow time-to-content. This can be mitigated with Server-Side Rendering (SSR) or Static Site Generation (SSG). Check out the [SSR Guide](/guide/scaling-up/ssr.html) for more details.
2482

25-
### Component and Reactivity Boundary
83+
## Update Optimizations
2684

27-
- Identify unnecessary updates with `renderTriggered`
85+
### Props Stability
2886

29-
### Provide / Inject
87+
In Vue, a child component only updates when at least one of its received props has changed. Consider the following example:
3088

31-
- Avoid props drilling
32-
- Avoid duplicated computed properties
89+
```vue-html
90+
<ListItem
91+
v-for="item in list"
92+
:id="item.id"
93+
:active-id="activeId" />
94+
```
95+
96+
Inside the `<ListItem>` component, it uses its `id` and `activeId` props to determine whether it is the currently active item. While this works, the problem is that whenever `activeId` changes, **every** `<ListItem>` in the list has to update!
97+
98+
Ideally, only the items whose active status changed should update. We can achieve that by moving the active status computation into the parent, and make `<ListItem>` directly accept an `active` prop instead:
99+
100+
```vue-html
101+
<ListItem
102+
v-for="item in list"
103+
:id="item.id"
104+
:active="item.id === activeId" />
105+
```
106+
107+
Now, for most components the `active` prop will remain the same when `activeId` changes, so they no longer need to update. In general, the idea is keeping the props passed to child components as stable as possible.
33108

34109
### `v-once`
35110

111+
`v-once` is a built-in directive that can be used to render content that relies on runtime data but never needs to update. The entire sub tree it is used on will be skipped for all future updates. Consult its [API reference](/api/built-in-directives.html#v-once) for more details.
112+
36113
### `v-memo`
114+
115+
`v-memo` is a built-in directive that can be used to conditionally skip the update of large sub trees or `v-for` lists. Consult its [API reference](/api/built-in-directives.html#v-memo) for more details.
116+
117+
## General Optimizations
118+
119+
> The following tips affect both page load and update performance.
120+
121+
### Virtualize Large Lists
122+
123+
One of the most common performance issues in all frontend applications is rendering large lists. No matter how performant a framework is, rendering a list with thousands of items **will** be slow due to the sheer amount of DOM nodes that the browser needs to handle.
124+
125+
However, we don't necessarily have to render all these nodes upfront. In most cases, the user's screen size can display only a small subset of our large list. We can greatly improve the performance with **list virtualization**, the technique of only rendering the items that are currently in or close to the viewport in a large list.
126+
127+
Implementing list virtualization isn't easy, luckily there are existing community libraries that you can directly use:
128+
129+
- [vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller)
130+
- [vue-virtual-scroll-grid](https://github.com/rocwang/vue-virtual-scroll-grid)
131+
132+
### Reduce Reactivity Overhead for Large Immutable Structures
133+
134+
Vue's reactivity system is deep by default. While this makes state management intuitive, it does create a certain level of overhead when the data size is large, because every property access triggers proxy traps that performs dependency tracking. This typically becomes noticeable when dealing with large arrays of deeply nested objects, where a single render needs to access 100,000+ properties, so it should only affect very specific use cases.
135+
136+
Vue does provide an escape hatch to opt-out of deep reactivity by using [`shallowRef()`](/api/reactivity-advanced.html#shallowref) and [`shallowReactive()`](/api/reactivity-advanced.html#shallowreactive). Shallow APIs create state that is reactive only at the root level, and exposes all nested objects untouched. This keeps nested property access fast, with the trade-off being that we must now treat all nested objects as immutable, and updates can only be triggered by replacing the root state:
137+
138+
```js
139+
const shallowArray = shallowRef([
140+
/* big list of deep objects */
141+
])
142+
143+
// this won't trigger updates...
144+
shallowArray.value.push(newObject)
145+
// this does:
146+
shallowArray.value = [...shallowArr.value, newObject]
147+
148+
// this won't trigger update...
149+
shallowArray.value[0].foo = 1
150+
// this does:
151+
shallowArray.value = [
152+
{
153+
...shallowArray.value[0],
154+
foo: 1
155+
},
156+
...shallowArray.value.slice(1)
157+
]
158+
```
159+
160+
### Avoid Unnecessary Component Abstractions
161+
162+
Sometimes we may create [renderless components](/guide/components/slots.html#renderless-components) or higher-order components (i.e. components that render other components with extra props) for better abstraction or code organization. While there is nothing wrong with this, do keep in mind that component instances are much more expensive than plain DOM nodes, and creating too many of them due to abstraction patterns will incur performance costs.
163+
164+
Note that reducing only a few instances won't have noticeable effect, so don't sweat it if the component is rendered only a few times in the app. The best scenario to consider this optimization is again in large lists. Imagine a list of 100 items where each item component contains many child components. Removing one unnecessary component abstraction here could result in a reduction of hundreds of component instances.

src/guide/best-practices/production-deployment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ During development, Vue provides a number of features to improve the development
99
- Reactivity debugging hooks
1010
- Devtools integration
1111

12-
However, these features become useless in production. Some of the warning checks also introduce small runtime overhead. When deploying to production, we should drop all the unused, development-only code branches for smaller payload size and better performance.
12+
However, these features become useless in production. Some of the warning checks can also incur a small amount of performance overhead. When deploying to production, we should drop all the unused, development-only code branches for smaller payload size and better performance.
1313

1414
## Without Build Tools
1515

src/guide/extras/ways-of-using-vue.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ Pure client-side SPAs are problematic when the app is sensitive to SEO and time-
3333

3434
Vue provides first-class APIs to "render" a Vue app into HTML strings on the server. This allows the server to send back already-rendered HTML, allowing end users to see the content immediately while the JavaScript is being downloaded. Vue will then "hydrate" the application on the client side to make it interactive. This is called [Server-Side Rendering (SSR)](/guide/scaling-up/ssr) and it greatly improves Core Web Vital metrics such as [Largest Contentful Paint (LCP)](https://web.dev/lcp/).
3535

36-
There are higher-level Vue-based frameworks built on top of this paradigm, such as [NuxtJS](https://v3.nuxtjs.org/), which allow you to develop a fullstack application using Vue and JavaScript.
36+
There are higher-level Vue-based frameworks built on top of this paradigm, such as [Nuxt](https://v3.nuxtjs.org/), which allow you to develop a fullstack application using Vue and JavaScript.
3737

3838
## JAMStack / SSG
3939

4040
Server-side rendering can be done ahead of time if the required data is static. This means we can pre-render an entire application into HTML and serve them as static files. This improves site performance and makes deployment a lot simpler since we no longer need to dynamically render pages on each request. Vue can still hydrate such applications to provide rich interactivity on the client. This technique is commonly referred to as Static-Site Generation (SSG), also known as [JAMStack](https://jamstack.org/what-is-jamstack/).
4141

42-
The Vue team maintains a static-site generator called [VitePress](https://vitepress.vuejs.org/), which powers this website you are reading right now! In addition, [NuxtJS](https://v3.nuxtjs.org/) also supports SSG. You can even mix SSR and SSG for different routes in the same Nuxt app.
42+
The Vue team maintains a static-site generator called [VitePress](https://vitepress.vuejs.org/), which powers this website you are reading right now! In addition, [Nuxt](https://v3.nuxtjs.org/) also supports SSG. You can even mix SSR and SSG for different routes in the same Nuxt app.
4343

4444
## Beyond the Web
4545

src/guide/scaling-up/ssr.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ A server-rendered Vue.js app can also be considered "isomorphic" or "universal",
1616

1717
Compared to a client-side Single-Page Application (SPA), the advantage of SSR primarily lies in:
1818

19+
- **Faster time-to-content**: this is more prominent on slow internet or slow devices. Server-rendered markup doesn't need to wait until all JavaScript has been downloaded and executed to be displayed, so your user will see a fully-rendered page sooner. In addition, data fetching is done on the server-side for the initial visit, which likely has a faster connection to your database than the client. This generally results in improved [Core Web Vitals](https://web.dev/vitals/) metrics, better user experience, and can be critical for applications where time-to-content is directly associated with conversion rate.
20+
21+
- **Unified mental model**: you get to use the same language and the same declarative, component-oriented mental model for developing your entire app, instead of jumping back and forth between a backend templating system and a frontend framework.
22+
1923
- **Better SEO**: the search engine crawlers will directly see the fully rendered page.
2024

2125
:::tip
22-
Note that as of now, Google and Bing can index synchronous JavaScript applications just fine. Synchronous being the key word there. If your app starts with a loading spinner, then fetches content via Ajax, the crawler will not wait for you to finish. This means if you have content fetched asynchronously on pages where SEO is important, SSR might be necessary.
26+
As of now, Google and Bing can index synchronous JavaScript applications just fine. Synchronous being the key word there. If your app starts with a loading spinner, then fetches content via Ajax, the crawler will not wait for you to finish. This means if you have content fetched asynchronously on pages where SEO is important, SSR might be necessary.
2327
:::
2428

25-
- **Faster time-to-content**: this is more prominent on slow internet or slow devices. Server-rendered markup doesn't need to wait until all JavaScript has been downloaded and executed to be displayed, so your user will see a fully-rendered page sooner. This generally results in improved [Core Web Vitals](https://web.dev/vitals/) metrics, better user experience, and can be critical for applications where time-to-content is directly associated with conversion rate.
26-
27-
- **Unified mental model**: you get to use the same language and the same declarative, component-oriented mental model for developing your entire app, instead of jumping back and forth between a backend templating system and a frontend framework.
28-
2929
There are also some trade-offs to consider when using SSR:
3030

3131
- Development constraints. Browser-specific code can only be used inside certain lifecycle hooks; some external libraries may need special treatment to be able to run in a server-rendered app.

0 commit comments

Comments
 (0)