包含循环引用的对象图上的Vue反应性

时间:2019-12-28 09:11:26

标签: vue.js vuejs2 tree vue-reactivity

先从某些上下文开始:

我正在基于VueJS构建一个mineswheeper应用程序。 我的网格是一棵对象树:每个Box对象都有一个“ neighbors”属性,其中包含Box对象,这些对象是紧随其后的Box。

结构是圆形的,但是很好。

现在是问题:

首先,我尝试在较小的网格(5x5)上运行良好,但是在尝试生成较大的网格(50x50)时,Vue在设置观察者时产生了“超出最大调用堆栈大小”错误,这是日志 Callstack error in console

树似乎太大了,以至于vue无法处理反应性。

已经冻结了我的对象,并且可以正常工作(没有调用堆栈错误):

data() {
    const game = new MinesWheeper(30, 30, 40)
    Object.freeze(game)
    return {
      game
    }
  }

但是由于冻结,反应性(显然)下降了。不过,我需要做出反应才能显示单击框。

现在这是我发现的问题/线索:

  • 您是否曾经遇到过Vue的调用堆栈问题,或者我错过了什么?

  • 是否可以通过这样的对象结构在VueJS上使反应性起作用? (VueX是解决方案的一部分吗?我真的不知道)

  • 还是我应该考虑使用VueJS以外的其他东西? (在香草味中做吗?)

预先感谢, 抱歉,如果我的帖子很乱,这是我5年来的第一次帖子,我很紧张大声笑

编辑:游戏对象的外观如下:

game: { // MinesWheeper
  _grid: { // Grid
    "_bombsNumber": 40
    _boxes: [ // Array of Box
      {
        "_hasBomb": true
        "_index": 0
        "_isRevealed": false
        "_neighbors": [Box, Box, Box, Box]
        "hasBomb": true
        "index": 0
        "isRevealed": false
        "nearBombs": 1
      }
    ]
  }
}

此外,在单击框时,我需要执行以下任一操作: -如果Box.hasBomb为true,则结束游戏 -显示内容(如果其中没有炸弹) -或在Box.nearBombs等于0时传播split()调用

我的Box.reveal()方法是递归的:

reveal() {
    if (this._isRevealed) return

    this._isRevealed = true

    if (this.hasBomb) {
      console.log('Game over')
    } else if (this.nearBombs === 0) {
      this._neighbors.forEach(neighbor => {
        neighbor.reveal()
      })
    }
  }

这就是为什么我认为我需要反应性来更新每个Box.reveal调用中的视图

1 个答案:

答案 0 :(得分:0)

因此,您具有Box对象的一维数组,并且每个对象都包含neighbors数组,该数组具有(相邻的Box的)引用(指向主数组项)。

您可以使用Object.defineProperty()neighbors创建configurable: false属性,而Vue将无法对其设置响应性。但是其余Box对象的属性仍然是反应性的。

我自己设置了一个little demo。重要的是在components/HelloWorld.vue组件中。

示例很少被混淆,因为使用数组可以通过索引轻松引用“ prev”和“ next”元素,但是我需要在对象之间引入循环引用。

在我的机器上,它将在数组中包含10000个项目时引发错误。但是,如果您用this.setupRelatioships(boxes);切换this.setupRelatioshipsWithProperties(boxes);呼叫,错误就会消失。

setupRelatioships(boxes) {
      for (let i = 0; i < boxes.length; i++) {
        boxes[i].prev = i > 0 ? boxes[i - 1] : null;
        boxes[i].next = i < boxes.length - 1 ? boxes[i + 1] : null;
      }
    },
    setupRelatioshipsWithProperties(boxes) {
      const opts = {
        configurable: false,
        enumerable: true,
        writable: true
      };

      for (let i = 0; i < boxes.length; i++) {
        Object.defineProperty(boxes[i], "prev", {
          ...opts,
          value: i > 0 ? boxes[i - 1] : null
        });
        Object.defineProperty(boxes[i], "next", {
          ...opts,
          value: i < boxes.length - 1 ? boxes[i + 1] : null
        });
      }
    },

以前引用的Demo(归功于Guillaume Chau