47

I have a categories array, which is loaded once (in created hook) and then it is static all the time. I render these array values in a component template.

<template>
    <ul>
        <li v-for="item in myArray">{{ item }}</li>
    </ul>
</template>

My data property looks (it does not include myArray - I don't want reactive binding):

data() {
    return {
        someReactiveData: [1, 2, 3]
    };
}

My create hook:

created() {
    // ...
    this.myArray = ["value 1", "value 2"];
    // ...
}

Problem is, that Vue throws error - I can't use myArray in a template, because this variable is not created when the DOM is created - mounted.

So how to do this? Or where can be stored component constants?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Dave
  • 473
  • 1
  • 4
  • 6

6 Answers6

70

Vue sets all the properties in the data option to setters/getters to make them reactive. See Reactivity in depth

Since you want myArray to be static you can create it as a custom option which can be accessed using vm.$options

export default{
    data() {
        return{
            someReactiveData: [1, 2, 3]
        }
    },
    //custom option name myArray
    myArray: null,
    created() {
        //access the custom option using $options
        this.$options.myArray = ["value 1", "value 2"];
    }
}

you can iterate over this custom options in your template as follows:

<template>
    <ul>
        <li v-for="item in $options.myArray">{{ item }}</li>
    </ul>
</template>

Here is the fiddle

tony19
  • 125,647
  • 18
  • 229
  • 307
Vamsi Krishna
  • 30,568
  • 8
  • 70
  • 78
  • 1
    Not so nice because it puts non-reactive data into another namespace $options, instead of mixing these with reactive data, which would be more preferable to update at once. – Dmitriy Sintsov Apr 12 '18 at 14:27
  • 13
    @DmitriySintsov I don't understand, isn't that the whole goal? A place to attach non-reactive data on the model? – zero298 Jun 28 '18 at 20:39
  • Is there a way to achieve the same with TypeScript? – megapixel23 Sep 23 '19 at 11:22
  • 1
    The way you describe it in your solution, `myArray` will be shared across all instances of that component. Is there a way to have non-reactive data per instance? – mediafreakch Apr 17 '20 at 17:03
  • @mediafreakch add a prop named `addNonReactive` and set it to be false by default. Use this prop `this.addNonReactive` in created hook to check whether add non reactive data or not. Pass true in component props if you want to add non reactive data. – Vamsi Krishna Apr 17 '20 at 17:45
  • solved it using the `beforeCreate` hook, where I just assign `this.myNonReactiveProp`. After that it is usable in templates, methods and other hooks. – mediafreakch Apr 17 '20 at 18:56
  • This works but is not TS-friendly, unfortunately – phil294 Nov 15 '21 at 22:39
  • @zero298 I'm late to the party, but I just stumbled upon a use case: storing large dynamic objects (like a Babylonjs scene) can be a problem since reactivity takes time, interrupting the rendering process. It's advised not to use in data. – bariscc Apr 08 '22 at 19:50
36

Actually, setting properties on this in created() should work out of the box:

<template>
  <div id="app">
    <ul>
      <li v-for="item in myArray" :key="item">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "app",
  created() {
    this.myArray = [
      'item 1',
      'item 2'
    ];
  }
};
</script>

will render

<div id="app">
  <ul>
    <li>
      item 1
    </li>
    <li>
      item 2
    </li>
  </ul>
</div>

Demo here: https://codesandbox.io/s/r0yqj2orpn .

BogdanL
  • 691
  • 5
  • 5
  • So if you add data with 'this' but it's not defined in the 'data' part of the component, it will be static data? – Sendai Apr 22 '22 at 18:33
  • `static` already has a meaning in Javascript (and class-based languages in general), where it means a value that's shared by all instances of a class. That's not what's happening here. `myArray` will *non-reactive*, i.e. Vue won't proxy it and watch it for changes. – Mud Sep 05 '22 at 17:27
7

I prefer using static data (non reactive) like this:

Create a mixin (i name it static_data.js) with the follow content

import Vue from 'vue'

Vue.prototype.$static = {}

export default {
  beforeCreate () {
    const vue_static = this.$options.static
    const vue_static_destination = this.$static || this

    if (vue_static && typeof(vue_static) === 'function') {
      Object.assign(vue_static_destination, vue_static.apply(this))
    } else if (vue_static && typeof(vue_static) === 'object') {
      Object.assign(vue_static_destination, vue_static)
    }      
  }
}

In your components where you want to use static data you can do:

import use_static_data from '@mixins/static_data'

export default {
  mixins: [use_static_data],

  static: () => ({
    static_value: 'Vue is awesome'
  }),

  created () {
    console.log(this.$static.static_value); // Vue is awesome
  }
}

There is also a package vue-static

Credits here.

Roland
  • 24,554
  • 4
  • 99
  • 97
6

If you want to keep it in data, the proper way is using Object.freeze(), as described in the documentation:

The only exception to this being the use of Object.freeze(), which prevents existing properties from being changed, which also means the reactivity system can’t track changes.

tony19
  • 125,647
  • 18
  • 229
  • 307
rpadovani
  • 7,101
  • 2
  • 31
  • 50
  • 1
    if the code is `data() { return { something: Object.freeze({1:2}) } }`, `this.something[1] = 3` will throw an error because the obj is freezed, but `this.something = {2:3}` will still work. So this isn't a good way to create non-reactive data – Jacob Goh May 15 '20 at 02:31
1

You can try this line of code. You can copy an object and remove the reactivity.

var newObj = JSON.parse(JSON.stringify(obj));
Zoren Konte
  • 306
  • 4
  • 12
0
 <template>
  <div id="app">
    <ul>
      <li v-for="item in myArray" :key="item">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "app",
  data () {
    this.myArray = [
      'item 1',
      'item 2'
    ];
    return {}
  }
};
</script>
Rinat Sadykov
  • 325
  • 3
  • 7