- The Vue Instance
- Template Syntax
- Computed Properties and Watchers
- Class and Style Bindings
- Conditional Rendering
- List Rendering
- Event Handling
- Form Input Bindings
- Components Basics
- Component Registration
- Custom Events
- Dynamic & Async Components
- Handling Edge Cases
Transitions & Animation
- Enter/Leave & List Transitions
- State Transitions
Reusability & Composition
- Custom Directives
- Render Functions & JSX
- Single File Components
- TypeScript Support
- Production Deployment
- State Management
- Server-Side Rendering
- Reactivity in Depth
- Migration from Vue 1.x
- Migration from Vue Router 0.7.x
- Migration from Vuex 0.6.x to 1.0
- Migration to Vue 2.7
- Comparison with Other Frameworks
- Join the Vue.js Community!
- Meet the Team
Handling Edge Cases
This page assumes you’ve already read the Components Basics. Read that first if you are new to components.
All the features on this page document the handling of edge cases, meaning unusual situations that sometimes require bending Vue’s rules a little. Note however, that they all have disadvantages or situations where they could be dangerous. These are noted in each case, so keep them in mind when deciding to use each feature.
In most cases, it’s best to avoid reaching into other component instances or manually manipulating DOM elements. There are cases, however, when it can be appropriate.
In every subcomponent of a
new Vue instance, this root instance can be accessed with the
$root property. For example, in this root instance:
All subcomponents will now be able to access this instance and use it as a global store:
This can be convenient for demos or very small apps with a handful of components. However, the pattern does not scale well to medium or large-scale applications, so we strongly recommend using Vuex to manage state in most cases.
$parent property can be used to access the parent instance from a child. This can be tempting to reach for as a lazy alternative to passing data with a prop.
In most cases, reaching into the parent makes your application more difficult to debug and understand, especially if you mutate data in the parent. When looking at that component later, it will be very difficult to figure out where that mutation came from.
<google-map> component might define a
map property that all subcomponents need access to. In this case
<google-map-markers> might want to access that map with something like
this.$parent.getMap, in order to add a set of markers to it. You can see this pattern in action here.
Keep in mind, however, that components built with this pattern are still inherently fragile. For example, imagine we add a new
<google-map-region> component and when
<google-map-markers> appears within that, it should only render markers that fall within that region:
<google-map-markers> you might find yourself reaching for a hack like this:
This has quickly gotten out of hand. That’s why to provide context information to descendant components arbitrarily deep, we instead recommend dependency injection.
ref attribute. For example:
Now in the component where you’ve defined this
ref, you can use:
to access the
<base-input> instance. This may be useful when you want to, for example, programmatically focus this input from a parent. In that case, the
<base-input> component may similarly use a
ref to provide access to specific elements inside it, such as:
And even define methods for use by the parent:
Thus allowing the parent component to focus the input inside
ref is used together with
v-for, the ref you get will be an array containing the child components mirroring the data source.
$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing
$refs from within templates or computed properties.
Earlier, when we described Accessing the Parent Component Instance, we showed an example like this:
In this component, all descendants of
<google-map> needed access to a
getMap method, in order to know which map to interact with. Unfortunately, using the
$parent property didn’t scale well to more deeply nested components. That’s where dependency injection can be useful, using two new instance options:
provide options allows us to specify the data/methods we want to provide to descendant components. In this case, that’s the
getMap method inside
Then in any descendants, we can use the
inject option to receive specific properties we’d like to add to that instance:
You can see the full example here. The advantage over using
$parent is that we can access
getMap in any descendant component, without exposing the entire instance of
<google-map>. This allows us to more safely keep developing that component, without fear that we might change/remove something that a child component is relying on. The interface between these components remains clearly defined, just as with
In fact, you can think of dependency injection as sort of “long-range props”, except:
- ancestor components don’t need to know which descendants use the properties it provides
- descendant components don’t need to know where injected properties are coming from
However, there are downsides to dependency injection. It couples components in your application to the way they’re currently organized, making refactoring more difficult. Provided properties are also not reactive. This is by design, because using them to create a central data store scales just as poorly as using
$root for the same purpose. If the properties you want to share are specific to your app, rather than generic, or if you ever want to update provided data inside ancestors, then that’s a good sign that you probably need a real state management solution like Vuex instead.
Learn more about dependency injection in the API doc.
So far, you’ve seen uses of
$emit, listened to with
v-on, but Vue instances also offer other methods in its events interface. We can:
- Listen for an event with
- Listen for an event only once with
- Stop listening for an event with
You normally won’t have to use these, but they’re available for cases when you need to manually listen for events on a component instance. They can also be useful as a code organization tool. For example, you may often see this pattern for integrating a 3rd-party library:
This has two potential issues:
- It requires saving the
pickerto the component instance, when it’s possible that only lifecycle hooks need access to it. This isn’t terrible, but it could be considered clutter.
- Our setup code is kept separate from our cleanup code, making it more difficult to programmatically clean up anything we set up.
You could resolve both issues with a programmatic listener:
Using this strategy, we could even use Pikaday with several input elements, with each new instance automatically cleaning up after itself:
See this example for the full code. Note, however, that if you find yourself having to do a lot of setup and cleanup within a single component, the best solution will usually be to create more modular components. In this case, we’d recommend creating a reusable
To learn more about programmatic listeners, check out the API for Events Instance Methods.
Note that Vue’s event system is different from the browser’s EventTarget API. Though they work similarly,
$off are not aliases for
Components can recursively invoke themselves in their own template. However, they can only do so with the
When you register a component globally using
Vue.component, the global ID is automatically set as the component’s
If you’re not careful, recursive components can also lead to infinite loops:
A component like the above will result in a “max stack size exceeded” error, so make sure recursive invocation is conditional (i.e. uses a
v-if that will eventually be
Let’s say you’re building a file directory tree, like in Finder or File Explorer. You might have a
tree-folder component with this template:
tree-folder-contents component with this template:
When you look closely, you’ll see that these components will actually be each other’s descendant and ancestor in the render tree - a paradox! When registering components globally with
Vue.component, this paradox is resolved for you automatically. If that’s you, you can stop reading here.
However, if you’re requiring/importing components using a module system, e.g. via Webpack or Browserify, you’ll get an error:
To explain what’s happening, let’s call our components A and B. The module system sees that it needs A, but first A needs B, but B needs A, but A needs B, etc. It’s stuck in a loop, not knowing how to fully resolve either component without first resolving the other. To fix this, we need to give the module system a point at which it can say, “A needs B eventually, but there’s no need to resolve B first.”
In our case, let’s make that point the
tree-folder component. We know the child that creates the paradox is the
tree-folder-contents component, so we’ll wait until the
beforeCreate lifecycle hook to register it:
Or alternatively, you could use Webpack’s asynchronous
import when you register the component locally:
inline-template special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring.
Your inline template needs to be defined inside the DOM element to which Vue is attached.
inline-template makes the scope of your templates harder to reason about. As a best practice, prefer defining templates inside the component using the
template option or in a
<template> element in a
Another way to define templates is inside of a script element with the type
text/x-template, then referencing the template by an id. For example:
Your x-template needs to be defined outside the DOM element to which Vue is attached.
These can be useful for demos with large templates or in extremely small applications, but should otherwise be avoided, because they separate templates from the rest of the component definition.
Thanks to Vue’s Reactivity system, it always knows when to update (if you use it correctly). There are edge cases, however, when you might want to force an update, despite the fact that no reactive data has changed. Then there are other cases when you might want to prevent unnecessary updates.
If you find yourself needing to force an update in Vue, in 99.99% of cases, you’ve made a mistake somewhere.
However, if you’ve ruled out the above and find yourself in this extremely rare situation of having to manually force an update, you can do so with
Rendering plain HTML elements is very fast in Vue, but sometimes you might have a component that contains a lot of static content. In these cases, you can ensure that it’s only evaluated once and then cached by adding the
v-once directive to the root element, like this:
Once again, try not to overuse this pattern. While convenient in those rare cases when you have to render a lot of static content, it’s simply not necessary unless you actually notice slow rendering – plus, it could cause a lot of confusion later. For example, imagine another developer who’s not familiar with
v-once or simply misses it in the template. They might spend hours trying to figure out why the template isn’t updating correctly.