1. Boundary issues
Documented here are all functions related to handling edge cases, i.e. some special cases that require some small tweaks to Vue's rules. However, note that these functions are all disadvantageous or dangerous scenarios. We will note it in each case, so please be careful when you use each function.
1. Access elements & components
1) Access the root instance
In the child component of each new Vue
instance , its root instance can be accessed through the $root
property. For example, in this root instance:
// Vue 根实例
new Vue({
data: {
foo: 1
},
computed: {
bar: function () { /* ... */ }
}
methods: {
baz: function () { /* ... */ }
}
})
All child components can access or use this instance as a global store.
|
This is handy for demos or very small applications with few components. However, this model is not the case when it is extended to medium and large applications. Therefore, in the vast majority of cases, we strongly recommend using Vuex to manage the state of the application.
2) Access the parent component instance
$root
Similarly , $parent
properties can be used to access instances of the parent component from a child component. It provides an opportunity to reach the parent component at any time later, instead of passing data to the child component as a prop.
Also in some cases where it might be appropriate, you need to share some component libraries specifically. For example, within an abstract component that interacts with a JavaScript API without rendering HTML, like these hypothetical Google Maps components:
<google-map>
<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map>
This <google-map>
component can define a map
property that all child components need to access . In this case you <google-map-markers>
might want to this.$parent.getMap
access that map in a similar way to add a set of markers to it. You can check out this pattern here .
Note, however, that the internals of the component built with this pattern are still prone to problems. For example, imagine we add a new <google-map-region>
component that, when <google-map-markers>
present inside it, only renders the markup in that area:
<google-map>
<google-map-region v-bind:shape="cityBoundaries">
<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map-region>
</google-map>
Then <google-map-markers>
internally you may find yourself needing some hacks like this:
var map = this.$parent.map || this.$parent.$parent.map
Soon it will get out of hand. That's why we recommend dependency injection when you need to provide contextual information to any deeper component .
3) Access child component instance or child element
Despite the existence of props and events, there may be times when you need to access a child component directly in JavaScript. For this purpose, you can assign an ID reference to the child component via the ref
attribute . E.g:
<base-input ref="usernameInput"></base-input>
Now in ref
your you can use:
<strong>this.$refs.usernameInput</strong>
to access this <base-input>
instance for emergencies. Such as programmatically focusing the input from a parent component. In the previous example, the <base-input>
component could also use a similar to ref
provide access to the specified element inside, for example:
This allows the parent component to <base-input>
focus the input field in :
this.$refs.usernameInput.focus()
ref
When v-for
used with and , the reference you get will be an array containing these subcomponents for the corresponding data source.
$refs
Only take effect after the component has been rendered, and they are not reactive. It just means an "escape hatch" encapsulated by a direct child component - you should avoid accessing it in templates or computed properties $refs
.
4) Dependency Injection
In the above example of Google Maps when accessing the parent instance:
<google-map>
<google-map-region v-bind:shape="cityBoundaries">
<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map-region>
</google-map>
In this component, <google-map>
all descendants of , need to access a getMap
method in order to know which map to interact with. Unfortunately, using the $parent
prop doesn't scale well to deeper nested components. This is also where dependency injection comes in, using two new instance options: provide
and inject
.
provide
Options allow us to specify the data/methods we want to provide to descendant components. In this case, it's the <google-map>
inner getMap
method:
provide: function () {
return {
getMap: this.getMap
}
}
Then in any descendant component, we can use options to receive specific properties we want to add to this instance: inject
inject: ['getMap']
2. Programmatic event listeners
Now that you know $emit
the usage of , it can be v-on
listened to, but the Vue instance also provides other methods in its event interface. We can:
- by
$on(eventName, eventHandler)
listening to an event $once(eventName, eventHandler)
By listening to an event at a time$off(eventName, eventHandler)
Stop listening to an event by
You won't normally use these, but they are useful when you need to manually listen for events on a component instance. They can also be used in code organization tools. For example, you may often see this pattern of integrating a third-party library: the following code is a bit confusing:
https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E9%80%92%E5%BD%92%E7%BB%84%E4%BB%B6
above this
3. Circular references
1) Recursive components
Components can call themselves from their own templates. But they can only do this with name
options :
|
When you register a component with Vue.component
global , the global ID is automatically set as the component's name
option.
|
With little care, recursive components can lead to infinite loops:
|
Components like the above will result in "max stack size exceeded" errors, so make sure the recursive call is conditional (eg using one false
that v-if
).