25
votes

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?

4

4 Answers

41
votes

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

6
votes

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.

5
votes

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.

1
votes

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)

}