如何在Vue 2中设置组件的非响应式数据?

48

我有一个分类数组,它只在创建钩子中加载一次,之后始终保持静态。我在组件模板中呈现这些数组值。

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

我的数据属性看起来像这样(它不包括我的数组 - 我不希望进行响应式绑定):

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

我的创建钩子:

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

问题是,Vue会抛出错误-我无法在模板中使用myArray,因为此变量在DOM创建时未被创建-mouted。

那么该如何处理?或者组件常量可以存储在哪里?


为什么不使用会话/session或本地/local存储? - samayo
1
有关此主题的更多阅读材料可在Vue问题https://github.com/vuejs/vue/issues/1988中获得。 - Michael Radionov
6个回答

71

Vue将data选项中的所有属性都设置为setter/getter以使它们具有响应性。请参见响应系统详解

如果您想让myArray保持静态,可以将其创建为自定义选项,然后可以使用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"];
    }
}

您可以按照以下方式在模板中迭代这些自定义选项:

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

这里是 fiddle


1
不太好,因为它将非响应式数据放入另一个命名空间$options中,而不是将其与响应式数据混合在一起,这样一次性更新会更可取。 - Dmitriy Sintsov
13
我会尽力进行翻译:@DmitriySintsov,我不明白,这不是整个目标吗?一个可以在模型上附加非响应式数据的地方? - zero298
有没有一种方法可以使用TypeScript实现相同的功能? - megapixel23
1
在你的解决方案中描述的方式,myArray 将在该组件的所有实例之间共享。有没有一种方法可以每个实例拥有非响应式数据? - mediafreakch
这个可以工作,但不幸的是它不太友好于TS。 - phil294
显示剩余3条评论

37

实际上,在created()中设置this的属性应该可以直接使用:

<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>

将被渲染

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

这里有个演示:https://codesandbox.io/s/r0yqj2orpn


所以,如果您使用“this”添加数据,但它在组件的“data”部分中未定义,那么它将成为静态数据? - Sendai
static 在 Javascript(以及基于类的语言)中已经有了一个含义,它表示一个被所有类实例共享的值。但这并不是这里发生的事情。myArray 将是非响应式的,也就是说 Vue 不会代理它并监视其变化。 - Mud

7

我更喜欢使用静态数据(非响应式)如下:

创建一个混入对象(我将其命名为static_data.js),其内容如下:

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)
    }      
  }
}

在您想使用静态数据的组件中,可以这样做:
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
  }
}

还有一个包vue-static与此相关。

感谢这里的贡献者。


6
如果您想将其保留在 data 中,正确的方法是使用Object.freeze(),如文档所述:

唯一的例外是使用Object.freeze(),它可以防止更改现有属性,这也意味着响应性系统无法跟踪更改。


1
如果代码是 data() { return { something: Object.freeze({1:2}) } }, 那么 this.something[1] = 3 会抛出错误,因为对象被冻结了, 但是 this.something = {2:3} 仍然可以工作。所以这不是创建非响应式数据的好方法。 - Jacob Goh

1
你可以尝试这行代码。你可以复制一个对象并去除响应性。
var newObj = JSON.parse(JSON.stringify(obj));

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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接