在VueJs的对象中添加新属性时,如何触发更改?

时间:2018-07-18 21:58:19

标签: vue.js vuejs2 vue-component vuex watch

我具有以下vuex状态的初始结构(在VueJS项目中):

state: {
    level1prop: null
}

然后我对其进行动态更改,并将其变异为以下结构:

state: {
    level1prop: {
        level2prop: {
            level3prop: {
                "customKey1": { /* this is some object 1 */ },
                "customKey2": { /* this is some object 2 */ },
                ...
            }
        }
    }
}

然后,我将在"customKeyN": { /* this is some object N */ }下添加level3prop,对我来说重要的是每次更改都会触发观察者,监视者正在监视level1prop从{状态。

最初,在我的变异中,我通过以下方式进行此更新:

if (!state.hasOwnProperty("level1prop"))
    state["level1prop"] = {};
else if (state["level1prop"] === null || state["level1prop"] === undefined)
    state["level1prop"] = {};

if (!state["level1prop"].hasOwnProperty("level2prop"))
    state["level1prop"]["level2prop"] = {};
else if (state["level1prop"]["level2prop"] === null || state["level1prop"]["level2prop"] === undefined)
    state["level1prop"]["level2prop"] = {};

if (!state["level1prop"]["level2prop"].hasOwnProperty("level3prop"))
    state["level1prop"]["level2prop"]["level3prop"] = {};
else if (state["level1prop"]["level2prop"]["level3prop"] === null || state["level1prop"]["level2prop"]["level3prop"] === undefined)
    state["level1prop"]["level2prop"]["level3prop"] = {};

let payloadObj = {  "customKey1": { /* this is some object 1 */ }  };
state["level1prop"]["level2prop"]["level3prop"] = payloadObj;

这正在按照我想要的方式创建结构,但是不会触发更改观察者。遵循here的建议,我将代码重构为几种不同的方式,但是没有一种触发更改。这是我尝试过的最新选项的示例:

if (!state.hasOwnProperty("level1prop"))
    state = Object.assign(state, { "level1prop" : {} });
else if (state["level1prop"] === null || state["level1prop"] === undefined)
    state = Object.assign(state, { "level1prop" : {} });

if (!state["level1prop"].hasOwnProperty("level2prop"))
    state["level1prop"] = Object.assign(state["level1prop"], { "level2prop" : {} });
else if (state["level1prop"]["level2prop"] === null || state["level1prop"]["level2prop"] === undefined)
    state["level1prop"] = Object.assign(state["level1prop"], { "level2prop" : {} });

if (!state["level1prop"]["level2prop"].hasOwnProperty("level3prop"))
    state["level1prop"]["level2prop"] = Object.assign(state["level1prop"]["level2prop"], { "level3prop" : {} });
else if (state["level1prop"]["level2prop"]["level3prop"] === null || state["level1prop"]["level2prop"]["level3prop"] === undefined)
    state["level1prop"]["level2prop"] = Object.assign(state["level1prop"]["level2prop"], { "level3prop" : {} });

let payloadObj = {  "customKey 1": { /* this is some object 1 */ }  };
state["level1prop"]["level2prop"]["level3prop"] = Object.assign(state["level1prop"]["level2prop"]["level3prop"], payloadObj);

同样,这正在创建我需要的结构,但观察者仍未触发。我尝试过但未触发更改的其他一些选项是:

...
state["level1prop"]["level2prop"]["level3prop"] = Object.assign({}, state["level1prop"]["level2prop"]["level3prop"], payloadObj);
...

...
Object.assign(state["level1prop"]["level2prop"]["level3prop"], payloadObj);
...

在如此复杂的对象状态下,嵌套级别如此之多,有什么方法可以触发观察者进行更改吗?

1 个答案:

答案 0 :(得分:0)

如文档Object Change Detection Caveats部分所述,您最好使用专门设计的Vue设置器Vue.set来在以后的状态中添加子级别。

然后确保您的观察者指定了deep选项,以便您的子级别更改时可以正确触发该选项。

const store = new Vuex.Store({
  state: {
    level1prop: null,
  },
});

const state = store.state;

if (!state["level1prop"])
  Vue.set(state, "level1prop", {})

if (!state["level1prop"]["level2prop"])
  Vue.set(state["level1prop"], "level2prop", {})

if (!state["level1prop"]["level2prop"]["level3prop"])
  Vue.set(state["level1prop"]["level2prop"], "level3prop", {})

let payloadObj = {
  "customKey1": {
    hello: "world",
  },
};
state["level1prop"]["level2prop"]["level3prop"] = payloadObj;

setTimeout(() => {
  // Change an already existing key.
  state["level1prop"]["level2prop"]["level3prop"].customKey1.hello = "too";
}, 1000);

setTimeout(() => {
  // To add or remove keys, make sure to use again Vue.set or Vue.delete.
  state["level1prop"]["level2prop"]["level3prop"].customKey1.hello = "too";
  Vue.set(state["level1prop"]["level2prop"], "level3propSibling", {
    hi: "again",
  });
}, 2000);


new Vue({
  store: store,
  watch: {
    "$store.state": {
      // Make sure you specify the `deep` option
      deep: true,
      handler() {
        console.log(store.state);
      },
    },
  },
});
<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/vuex@3"></script>