28

I am reading up on Vue components, and find their explanation of why data needs to be a function somewhat confusing:

The root instance

var vm = new Vue({
  el: '#example',
  data: {
    message: 'here data is a property'
  }
})

A component

var vm = new Vue({
  el: '#example',
  data: function () {
     return {
       counter: 0
     }
  }
})

The Vue docs explain this difference by assigning a global counter variable to each component, and then they act surprised that each component shares that same data... Also they don't explain why they already use a function here.

var data = { counter: 0 }

Vue.component('simple-counter', {
  template: '<div>{{ counter }}</div >',
  data: function () {
    return data  
  }
})

Of course the data is shared now

<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>

When you reference a global object as your data source, it's no surprise that the components don't have their own data. That is also true for root Vue instances that have data as a property.

var mydata = { counter: 0 }

var vm1 = new Vue({
  el: '#example1',
  data: mydata
})

var vm2 = new Vue({
  el: '#example2',
  data: mydata
})

So I'm still left with the question why a component can't have a data property?

0

5 Answers 5

47

From my understanding of this, It's to save memory

Many frameworks, such as Angular 2 or, (at times) React, make each instance of a component a separate object. This means that everything each component needs is initialized for every component. Normally though, you really only need to keep a component’s data separate for each initialization. Methods and such stay the same.

Vue avoids that pitfall by having data be a function that returns an object. That allows separate components to have separate internal state without needing to fully re-instantiate the entire component. Methods, computed property definitions, and lifecycle hooks are created and stored only once, and run against every instance of a component.

See this

3
  • 2
    Great! I was faced with that question too when I started learning Vue Commented Oct 19, 2017 at 9:58
  • 2
    Can you tell me how there could be multiple instance of the same component? Any example? and even official document have the data as a property not a function. vuejs.org/v2/guide/events.html#Listening-to-Events Commented Jan 28, 2020 at 11:20
  • Dead links, in the answer and comment.
    – AliN11
    Commented Oct 15, 2022 at 12:39
8

The data option should always be a function in the context of components which returns a fresh object.

This precaution is made by vue. So whenever you define the object directly in the data option, vue will catch for making the mistake.

Components are never allowed to directly mutate its state. This prevents us from messing up and doing bad things where components do not have their own state.

If this precaution is not made by vue, then you will have a chance to mutate any other components that owns from the component and this would be a security issue.

Example from the documentation:

It’s good to understand why the rules exist though, so let’s cheat.

<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
var data = { counter: 0 }

Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // data is technically a function, so Vue won't
  // complain, but we return the same object
  // reference for each component instance
  data: function () {
    return data
  }
})

new Vue({
  el: '#example-2'
})

Since all three component instances share the same data object, incrementing one counter increments them all! Ouch. Let’s fix this by instead returning a fresh data object:

data: function () {
  return {
    counter: 0
  }
}

Now all our counters each have their own internal state.

4
  • Thanks, that makes a lot of sense. I use the "vue-class-component" now, and that seems to abstract away the idea of data being a function. All properties of the class are automatically converted to this function but you don't need to define that yourself. It looks like class Bla extends Vue { myproperty = "hello" }
    – Kokodoko
    Commented Mar 4, 2018 at 15:16
  • Because it's abstraction. You cannot mutate its data from outside. Hope, this makes sense. Commented Mar 4, 2018 at 15:19
  • Yes, I think the class component makes it even safer than regular Vue, since data is automatically a function.
    – Kokodoko
    Commented Mar 4, 2018 at 15:21
  • Yes, but here data is not a function but member of the class. Commented Mar 4, 2018 at 15:22
7

It must be a function because otherwhise the data will be shared among all instances of the component, as objects are call by reference rather than call by value. This does not only happen when you reference a global object but also when data is an object itself. If data is a factory-function that returns an object this object will be created from scratch every time you mount a new instance of the component instead of just passing a reference to the global data.

1
  • 3
    When the value of data is an object, it’s shared across all instances of a component... we want each component instance to only manage its own data. For that to happen, each instance must generate a unique data object. In JavaScript, this can be accomplished by returning the object in a function. Source: vuejs.org/v2/style-guide/#Component-data-essential
    – Felix
    Commented Mar 13, 2018 at 11:46
1

Because when Vue init data,

function initData(vm){

  let data = vm.$options.data

  data = vm._data = typeof data === ‘function’ ? getData(data, vm) : data || {}
   /*

     Because here,data is a reference from vm.$options.data, 
     if data is an object, 
     when there are many instances of this Component,
     they all use the same `data`


      if data is a function, Vue will use method getData( a wrapper to executing data function, adds some error handling)

      and return a new object, this object just belongs to current vm you are initializing

    */

   ……


  // observing data
  observe(data, true)

}

1

why Vue forces the data property to be a function is that each instance of a component should have its own data object. If we don’t do that, all instances will be sharing the same object and every time we change something, it will be reflected in all instances.

var vm = new Vue({
  el: '#example',
  **data: function () {
     return {
       counter: 0
     }**
  }

Not the answer you're looking for? Browse other questions tagged or ask your own question.