【问题标题】:Vue changes not detected on Array在 Array 上未检测到 Vue 更改
【发布时间】:2020-02-27 15:00:15
【问题描述】:

大家好,我正在使用 Vue,我正在尝试从平面列表中创建递归树,我想在有人点击它时调整每个项目的扩展属性,但由于某种原因它没有改变

在这个函数中发生了

  expandNode(item) {
      console.log("HERERERER");
      item.expand = false;
      this.$set(item, "expand", false);
    }

我希望我的 Array 具有反应性,但由于某种原因它不是,这可能是我重新打包数据的方式还是其他方式,有人可以看看吗??

这是我的 CodeSandbox

https://codesandbox.io/s/condescending-tree-51rbs

这是组件代码

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <tr v-for="(item ,index)  in flatArray" :key="index">
      <div class="item" @click="expandNode(item)">
        <div class="element" v-show="item.expand">
          {{ item.expand }}
          <span>{{ item.label }}</span>
        </div>
      </div>
    </tr>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
    data: { default: () => null, type: Array }
  },
  data() {
    return {
      flatArray: []
    };
  },
  mounted() {
    let arr = [];
    console.log("HERERER");
    this.recursive(this.data, arr, 0, null, -1);
    this.flatArray = arr;
    console.log(this.flatArray);
  },
  computed: {
    setPadding(item) {
      return `padding-left: ${item.level * 30}px;`;
    }
  },
  methods: {
    recursive(obj, newObj, level, parent, parentIndex) {
      obj.forEach(node => {
        if (node.children && node.children.length != 0) {
          node.level = level;
          node.leaf = false;
          node.expand = true;
          node.parent = parent;
          node.parentIndex = parent ? parentIndex : null;
          newObj.push(node);
          this.recursive(
            node.children,
            newObj,
            node.level + 1,
            node,
            newObj.indexOf(node)
          );
        } else {
          node.level = level;
          node.leaf = true;
          node.expand = true;
          node.parent = obj;
          node.parentIndex = parent ? parentIndex : null;
          newObj.push(node);
          return false;
        }
      });
    },
    expandNode(item) {
      console.log("HERERERER");
      item.expand = false;
      this.$set(item, "expand", false);
    }
  }
};
</script>

【问题讨论】:

  • 当您向阵列添加节点时,请尝试推送克隆 (const newNode = { ...node}) 而不仅仅是对现有对象的引用。
  • 请注意,set 确实适用于数组项 jsfiddle.net/sh0ber/tj5n174c

标签: javascript vue.js vuejs2


【解决方案1】:

您没有看到更新的原因是数组没有理由重新计算。您正在更新 this.$set(item, "expand", false); 一个非反应性对象。它不是响应式的原因是因为您在创建对象时没有使用$set 方法。

如果您在对象创建过程中正确使用$set,这就是它的样子。

    recursive(obj, newObj, level, parent, parentIndex) {
      obj.forEach(node => {
        this.$set(node, "level", level);
        this.$set(node, "expand", true);
        this.$set(node, "parentIndex", parent ? parentIndex : null);
        if (node.children && node.children.length !== 0) {
          this.$set(node, "leaf", false);
          this.$set(node, "parent", parent);
          newObj.push(node);
          this.recursive(
            node.children,
            newObj,
            node.level + 1,
            node,
            newObj.indexOf(node)
          );
        } else {
          this.$set(node, "leaf", true);
          this.$set(node, "parent", obj);
          newObj.push(node);
          return false;
        }
      });
    },

请注意,您现在可以使用item.expand = false

    expandNode(item) {
      item.expand = false;
      // this.$set(item, "expand", false);  <== not needed
    }

你可以在行动中看到here


或者,...

这是不依赖反应性的可能对您有用的代码

请注意:

  • 我正在使用this.flatArray = flattenTree(this.data); 重新计算和重新分配数组
  • 这个想法是嵌套对象是“真实的来源”,扁平化的数组可以让模板进行渲染。
<template>
  <div class="hello">
    <tr v-for="(item ,index) in flatArray" :key="index">
      <div
        @click="toggleExpandNode(item)"
        class="item"
        :style="{'margin-left':item.level * 1.6 +'em'}"
      >
        <div class="element">
          <span v-if="item.leaf">&#9900;</span>
          <span v-else-if="item.expand">&#9662;</span>
          <span v-else>&#9656;</span>
          &nbsp;
          <span>{{ item.label }}</span>
        </div>
      </div>
    </tr>
  </div>
</template>

<script>
const flattenTree = obj => {
  const flatTreeArr = [];
  let depth = 0;

  const flatten = (node, parentNode) => {
    flatTreeArr.push(node);
    node.level = depth;
    node.leaf = true;
    node.parent = parentNode;
    node.expand = node.expand === undefined ? true : node.expand;
    if (node.children) {
      node.leaf = false;
      if (node.expand) {
        depth++;
        node.children.forEach(br => flatten(br, node));
        depth--;
      }
    }
  };

  obj.forEach(br => flatten(br, null));
  return flatTreeArr;
};

export default {
  props: {
    data: { default: () => null, type: Array }
  },
  data() {
    return {
      flatArray: []
    };
  },
  mounted() {
    this.flatArray = flattenTree(this.data);
  },
  methods: {
    toggleExpandNode(item) {
      item.expand = !item.expand;
      this.flatArray = flattenTree(this.data);
    }
  }
};
</script>

看到它在行动here

【讨论】:

  • 非常感谢,马上查看:)
  • $set 方法更新了答案,这对于 vue 来说更“正确”,并且更好地解决了这个问题。
  • 在这种情况下使用递归不是更快吗?
  • 真的需要循环遍历整个数组吗?
【解决方案2】:

如果item 对象没有初始expand 属性,您必须使用this.$set(item, ...) 将其声明为可观察。

直接添加像item.expand = ... 这样的新属性会跳过这一步,this.$set 会忽略它。这样的属性不会是反应性的。

在此处了解更多信息:https://vuejs.org/v2/guide/reactivity.html

【讨论】:

  • 有趣的是,在 flatArrayarr 创建时,do 具有 expand 的项目。如果您创建 arr 而不使用道具中的节点,则一切正常。所以实际上他们在flatArray 中有一个初始的expand 属性是不够的,他们也不能使用以前的反应数据并添加没有set 的属性。
  • @Darius 你是什么意思他们不能使用以前的反应数据?
猜你喜欢
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-09
  • 2015-07-20
相关资源
最近更新 更多