一个类的多个实例与共享模块

5

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

下面我会尝试解释我的问题。虽然我的真实项目要大得多且更加复杂,但是我使用这个例子来进行说明。

版本1

main.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互相通信时会出现问题。如果我不创建新实例就无法在element.js中导入import Layer from './layer',也无法访问从应用程序的其他位置创建的图层。

版本 2

main.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;

有没有办法让两者兼得呢?我考虑过为每个应用程序实例创建一个“存储”,跟踪它自己的层和元素,但共享其功能,但我不确定该把它放在哪里,以及如何在整个应用程序中传递引用。

编辑

为了澄清,因为我在评论中得到了这个问题。实际上,这是一个更大的项目,有很多不同的模块以许多不同的方式相互交互。上面的最小版本仅用于此问题的目的,但以下是一些其他部分,作为layer.jselement.js之间交互的示例。

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

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

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
@Bergi 那么我如何在它们(“子组件”)之间进行通信呢? - Jonatas Walker
1
那与通信无关。使用new调用的构造函数总是返回新实例,不需要覆盖它。 - Bergi

0

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

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

免责声明:我并不是自己想出这个解决方案的,而是借鉴了代码导师的建议。


main.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' 并将此函数分配给 window 对象。

这里有一个 jsfiddle,我在其中导入打包的库并创建了两个地图实例。

lib.mjs

import App from './app.mjs';

window.App = App;

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