Reactivity Fundamentals
Declaring Reactive State
Section titled “Declaring Reactive State”With the Options API, data
option are used to declare reactive state of a component. The option value should be a function that returns an object. Vue will call the function when creating a new component instance, and wrap the returned object in its reactivity system. Any top-level properties of this object are proxied on the component instance (this
in methods and lifecycle hooks):
export default { data() { return { count: 1 } }, // mounted is a lifecycle hook which we will explain later mounted() { // this refers to the component instance. console.log(this.count) // => 1
// data can be mutated as well this.count = 2 }}
Counter : 2
Try it in the playground
These instance properties are only added when the instance is first created, so you need to ensure they are all present in the object returned by the data
function. Where necessary, use null
, undefined
or some other placeholder value for properties where the desired value isn’t yet available.
It is possible to add a new property directly to this
without including it in data
. However, properties added this way will not be able to trigger reactive updates.
Vue uses a $
prefix when exposing its own built-in APIs via the component instance. It also reserves the prefix _
for internal properties. You should avoid using names for top-level data
properties that start with either of these characters.
Reactive Proxy vs. Original
Section titled “Reactive Proxy vs. Original” export default {data() { return { someObject: {} }},mounted() { const newObject = {} this.someObject = newObject
console.log(newObject === this.someObject) // false}
When you access this.someObject
after assigning it, the value is a reactive proxy of the original newObject
. Unlike in Vue 2, the original newObject
is left intact and will not be made reactive: make sure to always access reactive state as a property of this
.
Declaring Methods
Section titled “Declaring Methods”Watch Free Tutorial Video On Methods in Vue 3 From Vue School
methods
option are used to declare methods on a component instance. This should be an object containing the desired methods:
export default {data() { return { count: 0 }},methods: { increment() { this.count++ }},mounted() { // methods can be called in lifecycle hooks, or other methods! this.increment()}}
Vue automatically binds the this
value for methods
so that it always refers to the component instance.
This ensures that a method retains the correct this
value if it’s used as an event listener or callback.
You should avoid using arrow functions when defining methods
, as that prevents Vue from binding the appropriate this
value:
export default {methods: { increment: () => { // BAD: no this access here! }}}
Just like all other properties of the component instance, the methods
are accessible from within the component’s template. They are commonly used as event listeners inside templates:
<button @click="increment">{{ count }}</button>
In the example above, the method increment
will be called when the <button>
is clicked.
Try it in the playground
Deep Reactivity
Section titled “Deep Reactivity”In Vue, state is deeply reactive by default. This means you can expect changes to be detected even when you mutate nested objects or arrays:
export default {data() { return { obj: { nested: { count: 0 }, arr: ['foo', 'bar'] } }},methods: { mutateDeeply() { // these will work as expected. this.obj.nested.count++ this.obj.arr.push('baz') }}}
DOM Update Timing
Section titled “DOM Update Timing”When you mutate reactive state, the DOM is updated automatically. However, it should be noted that the DOM updates are not applied synchronously. Instead, Vue buffers them until the “next tick” in the update cycle to ensure that each component updates only once no matter how many state changes you have made.
To wait for the DOM update to complete after a state change, you can use the nextTick() global API:
import { nextTick } from 'vue'
export default {methods: { async increment() { this.count++ await nextTick() // Now the DOM is updated }}}
Stateful Methods
Section titled “Stateful Methods”In some cases, you may need to dynamically create a method function, for example creating a debounced event handler:
import { debounce } from 'lodash-es'
export default {methods: { // Debouncing with Lodash click: debounce(function () { // ... respond to click ... }, 500)}}
However, this approach is problematic for components that are reused because a debounced function is stateful: it maintains some internal state on the elapsed time. If multiple component instances share the same debounced function, they will interfere with one another.
To keep each component instance’s debounced function independent of the others, you can create the debounced version in the created
lifecycle hook:
export default {created() { // each instance now has its own copy of debounced handler this.debouncedClick = _.debounce(this.click, 500)},unmounted() { // also a good idea to cancel the timer // when the component is removed this.debouncedClick.cancel()},methods: { click() { // ... respond to click ... }}}