【问题标题】:Multiple instances of a class with shared modules具有共享模块的类的多个实例
【发布时间】:2015-12-19 10:01:52
【问题描述】:

我在运行一个类的多个实例时遇到了一些问题,同时使用 ES6 模块来获得更清晰的项目结构。

我将尝试在下面解释我的问题。我的实际项目更大更复杂,但我以这个为例。

版本 1

ma​​in.js

import App from './app';

let instance1 = new App,
    instance2 = new App;

app.js

import Layer from './layer'
import Element from './element'

class App {
  constructor() {
    this.layer = new Layer;
    this.element = new Element;
  }
  addLayer() {
    this.layer.addLayer();  
  }
  addElement() {
    this.element.addElement();
  }
}

module.exports = App;

layer.js

class Layer {
  constructor() {
    this.layers = [];
  }
  addLayer(name) {
    this.layers.push({
        name: name
    });
  }
}

module.exports = Layer;

element.js

class Element {
  constructor() {
    this.elements = [];
  }
  addElement(name) {
    this.elements.push({
      name: name
    });
  }
}

module.exports = Element;

现在我得到了instance1instance2 的唯一实例,包含它自己的图层和元素。当 element.jslayer.js 想要互相交谈时,问题就来了。我不能import Layer from './layer' inside element.js 而不必创建一个新实例,也无法访问从应用程序内其他位置创建的图层。

版本 2

ma​​in.js

import App from './app';

let instance1 = new App,
    instance2 = new App;

app.js

import layer from './layer'
import element from './element'

class App {
  addLayer() {
    layer.addLayer();  
  }
  addElement() {
    element.addElement();
  }
}

module.exports = App;

layer.js

class Layer {
  constructor() {
    this.layers = [];
  }
  addLayer(name) {
    this.layers.push({
      name: name
    });
  }
}

let layer = new Layer;
module.exports = layer;

element.js

class Element {
  constructor() {
    this.elements = [];
  }
  addElement(name) {
    this.elements.push({
      name: name
    });
  }
}

let element = new Element;
module.exports = element;

在这种情况下,我可以从 elements.js 访问图层,反之亦然,但它们也将在应用程序的两个实例之间共享,这不是我想要的。

它最初是一个单一的 app.js,它包含所有内容并且运行良好,但随着项目的发展,它变得非常难以维护。但下面是我想要实现的一个例子。

app.js

class App {
  constructor() {
    this.layers = [];
    this.elements = [];
  }
  addLayer(name) {
    this.layers.push({
      name: name
    });
  }
  addElement(name) {
    this.elements.push({
      name: name
    });
  }
}

module.exports = App;

有没有办法在这里两全其美?我已经为应用程序的每个实例考虑了类似 store 的东西,跟踪它自己的图层和元素,但共享它的功能,但我不确定将它放在哪里以及如何传递在应用程序周围引用它。

编辑

澄清一下,因为我在 cmets 中得到了这个问题。实际上,这是一个更大的项目,其中包含许多不同的模块,它们以多种不同的方式相互交互。上面的最小版本只是为了这个问题的目的,但这里有一些附加部分作为 layer.jselement.js 之间交互的示例。

ma​​in.js

instance1.element.get('element1').addToLayer('background');

layer.js

.addLayer(name) {
    this.layers.push({
        name: name,
        elements: [],
        add: (element) => {
            this.elements.push(element);
        }
    });
}

element.js

import layer from './layer';

...

.get(name) {
    let el;
    for(item in this.elements) {
        if(item.name == name) {
            el = item;
            break;
        }
    }
    return el;
}
.addElement(name) {
    this.elements.push({
        name: name,
        addToLayer: (name) => {
            let l;
            for(item in layer.layers) {
               if(item.name == name {
                   l = item;
                   break;
               }
            }
            if(l) l.add(this);
        }
    });
}

版本 1 中,layer.layers 在此处为空,因为我必须创建它的新实例。在版本 2 中,它可以正常工作,但 App 的所有实例将共享相同的 layer.layers

【问题讨论】:

  • 我不明白。为什么层和元素需要相互交流?他们之间有什么关系,你能举一个他们互动的例子吗?
  • 当然!一个层可以包含元素,因此该元素可能有一个名为.addToLayer(name) 的方法,该方法应该将元素添加到该层。一个图层可能看起来像{ name: 'layer1', elements: [] },我希望能够访问图层数组,找到正确的图层并将元素添加到其中。但正如我所说,这是一个更大的应用程序的一个非常小的例子。实际上,大约有 20 个不同的模块彼此之间有很多交互。为了这个问题,我想尽可能地简化一切。
  • 我认为您需要将layer 对象(或整个App 实例)作为参数传递给addToLayer,否则它不知道在哪里解析名称。如果您不想显式传递它,则需要在构建层时传递引用,并为每个层存储它所属的应用程序。在这两种解决方案中,您都不需要将一个模块导入另一个模块,顺便说一句。
  • @Bergi 我一直在尝试在模块中处理实例,例如为每个实例保留一个 id 并传递它,或者拥有一个包含所有数组的存储模块并让 add(), get() 等保留在其他共享模块中。当然有办法让它发挥作用,但我不喜欢的是它再次变得非常复杂,几乎违背了我试图做的事情的目的。我有一种感觉,我不可能是唯一一个遇到这种情况的人,但也许我必须从头开始重新考虑整个结构。

标签: javascript module ecmascript-6


【解决方案1】:

版本 1

app.js

import Layer from './layer'
import Element from './element'

class App {
  constructor() {
    this.layer = new Layer(this);
    this.element = new Element(this);
  }
  addLayer() {
    this.layer.addLayer();  
  }
  addElement() {
    this.element.addElement();
  }
}

module.exports = App;

layer.js

class Layer {
  constructor(app) {
    this.App = app;
    this.layers = [];
  }
  addLayer(name) {
    this.layers.push({
        name: name
    });
    this.App.element.addElement(name); // Element method
  }
  foo() {
    console.log('Hey you!!');
  }
}

module.exports = Layer;

element.js

class Element {
  constructor(app) {
    this.App = app;
    this.elements = [];
  }
  addElement(name) {
    // ...
    this.App.layer.foo(); // I can talk with Layer class
  }
}

module.exports = Element;

另见https://github.com/jonataswalker/es6-sample-project

【讨论】:

  • 你应该在构造函数中省略return this
  • @Bergi 但是我该如何在他们(“孩子们”)之间进行交流?
  • 这与沟通无关。使用new always 调用的构造函数返回新实例,无需覆盖它。
【解决方案2】:

过去几天我一直在解决同样的问题,并想出了一个不需要使用类的方法。

我的完整解决方案是针对需要为具有多个地图的仪表板实例化的地图控件。

免责声明;我没有自己想出这个解决方案,而是使用了code mentor 的建议。


ma​​in.mjs

import App from './app.mjs';

const instance1 = App();

const instance2 = App();

app.mjs

import Layer from './layer.mjs';

import Element from './element.mjs';

export default () => {

  const app = {};

  Layer(app);

  Element(app);

  return app;

}

layer.mjs / element.mjs

export default app => {

  app.layers = [];

  app.addLayer = name => {
    app.layer.push({
      name: name
    })
  }

}

另外,我创建了一个 lib.mjs 文件,我将它与 Webpack 捆绑并用作源库文件。在这里导入实例构造函数'App'并将这个函数分配给窗口对象。

Here is a jsfiddle in which I import the bundled library and create two map instances.

lib.mjs

import App from './app.mjs';

window.App = App;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 2021-11-19
    • 2023-03-29
    • 1970-01-01
    • 2019-09-19
    相关资源
    最近更新 更多