matijao/webdev huggingface dataset:
Animation Techniques | Vue.js

Animation Techniques
​

Vue provides the <Transition> and <TransitionGroup> components for handling enter / leave and list transitions. However, there are many other ways of using animations on the web, even in a Vue application. Here we will discuss a few additional techniques.

Class-based Animations
​

For elements that are not entering / leaving the DOM, we can trigger animations by dynamically adding a CSS class:

js
const disabled = ref(false)

function warnDisabled() {
  disabled.value = true
  setTimeout(() => {
    disabled.value = false
  }, 1500)
}
template
<div :class="{ shake: disabled }">
  <button @click="warnDisabled">Click me</button>
  <span v-if="disabled">This feature is disabled!</span>
</div>
css
.shake {
  animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  transform: translate3d(0, 0, 0);
}

@keyframes shake {
  10%,
  90% {
    transform: translate3d(-1px, 0, 0);
  }

  20%,
  80% {
    transform: translate3d(2px, 0, 0);
  }

  30%,
  50%,
  70% {
    transform: translate3d(-4px, 0, 0);
  }

  40%,
  60% {
    transform: translate3d(4px, 0, 0);
  }
}
Click me
State-driven Animations
​

Some transition effects can be applied by interpolating values, for instance by binding a style to an element while an interaction occurs. Take this example for instance:

js
const x = ref(0)

function onMousemove(e) {
  x.value = e.clientX
}
template
<div
  @mousemove="onMousemove"
  :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }"
  class="movearea"
>
  <p>Move your mouse across this div...</p>
  <p>x: {{ x }}</p>
</div>
css
.movearea {
  transition: 0.3s background-color ease;
}

Move your mouse across this div...

x: 0

In addition to color, you can also use style bindings to animate transform, width, or height. You can even animate SVG paths using spring physics - after all, they are all attribute data bindings:

Drag Me
Source code
Animating with Watchers
​

With some creativity, we can use watchers to animate anything based on some numerical state. For example, we can animate the number itself:

js
import { ref, reactive, watch } from 'vue'
import gsap from 'gsap'

const number = ref(0)
const tweened = reactive({
  number: 0
})

watch(number, (n) => {
  gsap.to(tweened, { duration: 0.5, number: Number(n) || 0 })
})
template
Type a number: <input v-model.number="number" />
<p>{{ tweened.number.toFixed(0) }}</p>
Type a number: 

0

Try it in the Playground
Production Deployment | Vue.js

Production Deployment
​
Development vs. Production
​

During development, Vue provides a number of features to improve the development experience:

Warning for common errors and pitfalls
Props / events validation
Reactivity debugging hooks
Devtools integration

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.

Without Build Tools
​

If you are using Vue without a build tool by loading it from a CDN or self-hosted script, make sure to use the production build (dist files that end in .prod.js) when deploying to production. Production builds are pre-minified with all development-only code branches removed.

If using global build (accessing via the Vue global): use vue.global.prod.js.
If using ESM build (accessing via native ESM imports): use vue.esm-browser.prod.js.

Consult the dist file guide for more details.

With Build Tools
​

Projects scaffolded via create-vue (based on Vite) or Vue CLI (based on webpack) are pre-configured for production builds.

If using a custom setup, make sure that:

vue resolves to vue.runtime.esm-bundler.js.
The compile time feature flags are properly configured.
process.env.NODE_ENV is replaced with "production" during build.

Additional references:

Vite production build guide
Vite deployment guide
Vue CLI deployment guide
Tracking Runtime Errors
​

The app-level error handler can be used to report errors to tracking services:

js
import { createApp } from 'vue'

const app = createApp(...)

app.config.errorHandler = (err, instance, info) => {
  // report error to tracking services
}

Services such as Sentry and Bugsnag also provide official integrations for Vue.
Routing | Vue.js

Routing
​
Client-Side vs. Server-Side Routing
​

Routing on the server side means the server sending a response based on the URL path that the user is visiting. When we click on a link in a traditional server-rendered web app, the browser receives an HTML response from the server and reloads the entire page with the new HTML.

In a Single-Page Application (SPA), however, the client-side JavaScript can intercept the navigation, dynamically fetch new data, and update the current page without full page reloads. This typically results in a more snappy user experience, especially for use cases that are more like actual "applications", where the user is expected to perform many interactions over a long period of time.

In such SPAs, the "routing" is done on the client side, in the browser. A client-side router is responsible for managing the application's rendered view using browser APIs such as History API or the hashchange event.

Official Router
​
Watch a Free Video Course on Vue School

Vue is well-suited for building SPAs. For most SPAs, it's recommended to use the officially-supported Vue Router library. For more details, see Vue Router's documentation.

Simple Routing from Scratch
​

If you only need very simple routing and do not wish to involve a full-featured router library, you can do so with Dynamic Components and update the current component state by listening to browser hashchange events or using the History API.

Here's a bare-bone example:

vue
<script setup>
import { ref, computed } from 'vue'
import Home from './Home.vue'
import About from './About.vue'
import NotFound from './NotFound.vue'

const routes = {
  '/': Home,
  '/about': About
}

const currentPath = ref(window.location.hash)

window.addEventListener('hashchange', () => {
  currentPath.value = window.location.hash
})

const currentView = computed(() => {
  return routes[currentPath.value.slice(1) || '/'] || NotFound
})
</script>

<template>
  <a href="#/">Home</a> |
  <a href="#/about">About</a> |
  <a href="#/non-existent-path">Broken Link</a>
  <component :is="currentView" />
</template>

Try it in the Playground
TransitionGroup | Vue.js

TransitionGroup
​

<TransitionGroup> is a built-in component designed for animating the insertion, removal, and order change of elements or components that are rendered in a list.

Differences from <Transition>
​

<TransitionGroup> supports the same props, CSS transition classes, and JavaScript hook listeners as <Transition>, with the following differences:

By default, it doesn't render a wrapper element. But you can specify an element to be rendered with the tag prop.

Transition modes are not available, because we are no longer alternating between mutually exclusive elements.

Elements inside are always required to have a unique key attribute.

CSS transition classes will be applied to individual elements in the list, not to the group / container itself.

TIP

When used in in-DOM templates, it should be referenced as <transition-group>.

Enter / Leave Transitions
​

Here is an example of applying enter / leave transitions to a v-for list using <TransitionGroup>:

template
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item">
    {{ item }}
  </li>
</TransitionGroup>
css
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
Add at random indexRemove at random index
1
2
3
4
5
Move Transitions
​

The above demo has some obvious flaws: when an item is inserted or removed, its surrounding items instantly "jump" into place instead of moving smoothly. We can fix this by adding a few additional CSS rules:

css
.list-move, /* apply transition to moving elements */
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}

.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

/* ensure leaving items are taken out of layout flow so that moving
   animations can be calculated correctly. */
.list-leave-active {
  position: absolute;
}

Now it looks much better - even animating smoothly when the whole list is shuffled:

AddRemoveShuffle
1
2
3
4
5

Full Example

Staggering List Transitions
​

By communicating with JavaScript transitions through data attributes, it's also possible to stagger transitions in a list. First, we render the index of an item as a data attribute on the DOM element:

template
<TransitionGroup
  tag="ul"
  :css="false"
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @leave="onLeave"
>
  <li
    v-for="(item, index) in computedList"
    :key="item.msg"
    :data-index="index"
  >
    {{ item.msg }}
  </li>
</TransitionGroup>

Then, in JavaScript hooks, we animate the element with a delay based on the data attribute. This example is using the GreenSock library to perform the animation:

js
function onEnter(el, done) {
  gsap.to(el, {
    opacity: 1,
    height: '1.6em',
    delay: el.dataset.index * 0.15,
    onComplete: done
  })
}
Bruce Lee
Jackie Chan
Chuck Norris
Jet Li
Kung Fury

Full Example in the Playground

Related

<TransitionGroup> API reference
Lifecycle Hooks | Vue.js

Lifecycle Hooks
​

Each Vue component instance goes through a series of initialization steps when it's created - for example, it needs to set up data observation, compile the template, mount the instance to the DOM, and update the DOM when data changes. Along the way, it also runs functions called lifecycle hooks, giving users the opportunity to add their own code at specific stages.

Registering Lifecycle Hooks
​

For example, the onMounted hook can be used to run code after the component has finished the initial rendering and created the DOM nodes:

vue
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  console.log(`the component is now mounted.`)
})
</script>

There are also other hooks which will be called at different stages of the instance's lifecycle, with the most commonly used being onMounted, onUpdated, and onUnmounted.

When calling onMounted, Vue automatically associates the registered callback function with the current active component instance. This requires these hooks to be registered synchronously during component setup. For example, do not do this:

js
setTimeout(() => {
  onMounted(() => {
    // this won't work.
  })
}, 100)

Do note this doesn't mean that the call must be placed lexically inside setup() or <script setup>. onMounted() can be called in an external function as long as the call stack is synchronous and originates from within setup().

Lifecycle Diagram
​

Below is a diagram for the instance lifecycle. You don't need to fully understand everything going on right now, but as you learn and build more, it will be a useful reference.

Consult the Lifecycle Hooks API reference for details on all lifecycle hooks and their respective use cases.
Conditional Rendering | Vue.js

Conditional Rendering
​
Watch a free video lesson on Vue School
v-if
​

The directive v-if is used to conditionally render a block. The block will only be rendered if the directive's expression returns a truthy value.

template
<h1 v-if="awesome">Vue is awesome!</h1>
v-else
​

You can use the v-else directive to indicate an "else block" for v-if:

template
<button @click="awesome = !awesome">Toggle</button>

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
Toggle
Vue is awesome!

Try it in the Playground

A v-else element must immediately follow a v-if or a v-else-if element - otherwise it will not be recognized.

v-else-if
​

The v-else-if, as the name suggests, serves as an "else if block" for v-if. It can also be chained multiple times:

template
<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

Similar to v-else, a v-else-if element must immediately follow a v-if or a v-else-if element.

v-if on <template>
​

Because v-if is a directive, it has to be attached to a single element. But what if we want to toggle more than one element? In this case we can use v-if on a <template> element, which serves as an invisible wrapper. The final rendered result will not include the <template> element.

template
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-else and v-else-if can also be used on <template>.

v-show
​

Another option for conditionally displaying an element is the v-show directive. The usage is largely the same:

template
<h1 v-show="ok">Hello!</h1>

The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.

v-show doesn't support the <template> element, nor does it work with v-else.

v-if vs. v-show
​

v-if is "real" conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles.

v-if is also lazy: if the condition is false on initial render, it will not do anything - the conditional block won't be rendered until the condition becomes true for the first time.

In comparison, v-show is much simpler - the element is always rendered regardless of initial condition, with CSS-based toggling.

Generally speaking, v-if has higher toggle costs while v-show has higher initial render costs. So prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime.

v-if with v-for
​

Note

It's not recommended to use v-if and v-for on the same element due to implicit precedence. Refer to style guide for details.

When v-if and v-for are both used on the same element, v-if will be evaluated first. See the list rendering guide for details.
Custom Renderer API | Vue.js

Custom Renderer API
​
createRenderer()
​

Creates a custom renderer. By providing platform-specific node creation and manipulation APIs, you can leverage Vue's core runtime to target non-DOM environments.

Type

ts
function createRenderer<HostNode, HostElement>(
  options: RendererOptions<HostNode, HostElement>
): Renderer<HostElement>

interface Renderer<HostElement> {
  render: RootRenderFunction<HostElement>
  createApp: CreateAppFunction<HostElement>
}

interface RendererOptions<HostNode, HostElement> {
  patchProp(
    el: HostElement,
    key: string,
    prevValue: any,
    nextValue: any,
    // the rest is unused for most custom renderers
    isSVG?: boolean,
    prevChildren?: VNode<HostNode, HostElement>[],
    parentComponent?: ComponentInternalInstance | null,
    parentSuspense?: SuspenseBoundary | null,
    unmountChildren?: UnmountChildrenFn
  ): void
  insert(
    el: HostNode,
    parent: HostElement,
    anchor?: HostNode | null
  ): void
  remove(el: HostNode): void
  createElement(
    type: string,
    isSVG?: boolean,
    isCustomizedBuiltIn?: string,
    vnodeProps?: (VNodeProps & { [key: string]: any }) | null
  ): HostElement
  createText(text: string): HostNode
  createComment(text: string): HostNode
  setText(node: HostNode, text: string): void
  setElementText(node: HostElement, text: string): void
  parentNode(node: HostNode): HostElement | null
  nextSibling(node: HostNode): HostNode | null

  // optional, DOM-specific
  querySelector?(selector: string): HostElement | null
  setScopeId?(el: HostElement, id: string): void
  cloneNode?(node: HostNode): HostNode
  insertStaticContent?(
    content: string,
    parent: HostElement,
    anchor: HostNode | null,
    isSVG: boolean
  ): [HostNode, HostNode]
}

Example

js
import { createRenderer } from '@vue/runtime-core'

const { render, createApp } = createRenderer({
  patchProp,
  insert,
  remove,
  createElement
  // ...
})

// `render` is the low-level API
// `createApp` returns an app instance
export { render, createApp }

// re-export Vue core APIs
export * from '@vue/runtime-core'

Vue's own @vue/runtime-dom is implemented using the same API. For a simpler implementation, check out @vue/runtime-test which is a private package for Vue's own unit testing.
Built-in Special Attributes | Vue.js

Built-in Special Attributes
​
key
​

The key special attribute is primarily used as a hint for Vue's virtual DOM algorithm to identify vnodes when diffing the new list of nodes against the old list.

Expects: number | string | symbol

Details

Without keys, Vue uses an algorithm that minimizes element movement and tries to patch/reuse elements of the same type in-place as much as possible. With keys, it will reorder elements based on the order change of keys, and elements with keys that are no longer present will always be removed / destroyed.

Children of the same common parent must have unique keys. Duplicate keys will cause render errors.

The most common use case is combined with v-for:

template
<ul>
  <li v-for="item in items" :key="item.id">...</li>
</ul>

It can also be used to force replacement of an element/component instead of reusing it. This can be useful when you want to:

Properly trigger lifecycle hooks of a component
Trigger transitions

For example:

template
<transition>
  <span :key="text">{{ text }}</span>
</transition>

When text changes, the <span> will always be replaced instead of patched, so a transition will be triggered.

See also Guide - List Rendering - Maintaining State with key

ref
​

Denotes a template ref.

Expects: string | Function

Details

ref is used to register a reference to an element or a child component.

In Options API, the reference will be registered under the component's this.$refs object:

template
<!-- stored as this.$refs.p -->
<p ref="p">hello</p>

In Composition API, the reference will be stored in a ref with matching name:

vue
<script setup>
import { ref } from 'vue'

const p = ref()
</script>

<template>
  <p ref="p">hello</p>
</template>

If used on a plain DOM element, the reference will be that element; if used on a child component, the reference will be the child component instance.

Alternatively ref can accept a function value which provides full control over where to store the reference:

template
<ChildComponent :ref="(el) => child = el" />

An important note about the ref registration timing: because the refs themselves are created as a result of the render function, you must wait until the component is mounted before accessing them.

this.$refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

See also

Guide - Template Refs
Guide - Typing Template Refs 
Guide - Typing Component Template Refs 
is
​

Used for binding dynamic components.

Expects: string | Component

Usage on native elements 3.1+

When the is attribute is used on a native HTML element, it will be interpreted as a Customized built-in element, which is a native web platform feature.

There is, however, a use case where you may need Vue to replace a native element with a Vue component, as explained in in-DOM Template Parsing Caveats. You can prefix the value of the is attribute with vue: so that Vue will render the element as a Vue component instead:

template
<table>
  <tr is="vue:my-row-component"></tr>
</table>

See also

Built-in Special Element - <component>
Dynamic Components