如何实现一个插件,仅在需要时才修改原始模块?

时间:2014-12-17 19:32:36

标签: javascript requirejs

我有一个扩展原始模块的插件 它只应在明确要求时修改模块。

问题:
只要需要一次,原始模块就会永久修改,也适用于插件不是依赖项的情况 这里的顺序无关紧要,只需要一次插件即可。

示例:

define("main", [], function() {
    return {opt: "A"};
});
define("plugin", ["main"], function(obj) {
    obj.opt = "B";
});
require(["main", "plugin"], function(obj) {
    console.log(obj.opt); // should log B
});
require(["main"], function(obj) {
    console.log(obj.opt); // should log A but logs B
});

我想要走的路是以某种方式告诉要求始终从源​​代码重新加载main而不是使用缓存版本。
我不知道怎么样。
或者可能有一种更优雅的方式?

请赐教,伙计们。

小提琴:http://jsfiddle.net/r75e446f

更新:有些人可能会发现知道我需要在我的业力单元测试环境中测试带有和不带插件的模块这一点很重要。

UPDATE2:请查看我自己的解决方案。

5 个答案:

答案 0 :(得分:4)

RequireJS模块是单身人士。如果你加载main一次,两次,10次,你总是会得到相同的模块。因此,如果您修改其状态,则会对使用它的所有模块进行修改。有可能告诉RequireJS取消定义模块,但我不推荐它,因为它只会使你的代码模糊不清。

如果我想做你想做的事情,我会设计我的代码:

<script>
  define("main", [], function() {
      function Main (opt) {
          this.opt = opt;
      }
      return Main;
  });
  define("plugin1", [], function() {
      return {
          install: function (main) {
              main.opt += " plugin1";
          }
      };
  });
  define("plugin2", [], function() {
      return {
          install: function (main) {
              main.opt += " plugin2";
          }
      };
  });
  // Case 1: no plugins
  require(["main"], function(Main) {
      var main = new Main("A");
      console.log(main.opt);
  });
  // Case 2: only plugin1
  require(["plugin1", "main"], function (plugin1, Main) {
      var main = new Main("A");
      plugin1.install(main);
      console.log(main.opt);
  });
  // Case 3: only plugin2
  require(["plugin2", "main"], function (plugin2, Main) {
      var main = new Main("A");
      plugin2.install(main);
      console.log(main.opt);
  });
  // Case 4: plugin1 and plugin2
  require(["plugin1", "plugin2", "main"], function (plugin1, plugin2,
                                                    Main) {
      var main = new Main("A");
      plugin1.install(main);
      plugin2.install(main);
      console.log(main.opt);
  });

基本上,使所有情况的共同点Main类可以在构造初始化,可以通过插件进行修改。然后每个插件都可以在Main上自行安装。上面的代码是可以如何完成的最小例证。在实际项目中,最终的解决方案必须考虑到项目的具体需求。

答案 1 :(得分:3)

如果您不希望为使用它的所有模块修改原始模块,那么您的插件不应修改原始模块。相反,让插件返回原始模块的修改副本。

define("main", [], function() {
  return {opt: "A"};
});
define("plugin", ["main"], function(obj) {
  var decorator = {}
  for (var key in obj) { decorator[key] = obj[key];}
  decorator.opt = "B";
  return decorator
});
require(["main", "plugin"], function(obj, plugin) {
  console.log(plugin.opt); // should log B
});
require(["main"], function(obj) {
  console.log(obj.opt); // should log A but logs B
});

如果您的原始对象是一个没有任何函数的简单类似结构的对象,那么这将没有任何复杂性。如果有函数,或者您的原始对象是使用模块模式构造的,那么复制中很可能会出现细微的错误,具体取决于方法的定义方式。

编辑2015-01-13:OP澄清了他的问题,他希望他的测试方法能够运行修改和未修改的原始模块,而无需重新加载页面。在这种情况下,我建议使用require.undef卸载主模块,然后重新加载它而不必重新加载整个页面。

答案 2 :(得分:0)

我想提出我的讨论理解..当我们AMD,我们定义module。除非模块(在这种情况下为main,已定义的源)能够接受plugin并允许插件更改属性,我想我们无法劫持它或它将会反模式?

我们如何扩展/克隆定义的源,比如

define("plugin", ["main"], function(obj) {
    return $.extend(true, {}, obj, {opt: "B"});
});

http://jsfiddle.net/r75e446f/6/

此处plugin模块始终使用main模块作为其依赖项,然后在使用模块时,我们只需直接使用plugin模块,不再需要main模块1}}模块? (顺便说一句,我理解这个'插件'的定义,但我的方向是我们正在处理module作为requireJs设计的内容)

- 第二种方法 -

如果你真的想以插件的方式去做,那么loader plugin怎么样?就像text插件一样,我们总是使用`require('text!abc.json')。所以编写一个加载器并将其注册到requireJs的配置,然后我们可以使用它。 (more)

答案 3 :(得分:0)

所以我发现了如何实现,我想要什么,并认为我会在这里分享。

答案称为'context',这是requirejs config中的一个选项。

http://requirejs.org/docs/api.html#multiversion

以下是我实施解决方案的方法:

var reqOne = require.config({
    context: 'one'
});
var reqTwo = require.config({
    context: 'two'
});
reqOne(["main", "plugin"], function(obj) {
    console.log(obj.opt); // logs B
});
reqTwo(["main"], function(obj) {
    console.log(obj.opt); // logs A
});

不幸的是,这在the fiddle中不起作用,因为第二个要求会尝试从外部加载主模块,我无法将文件上传到jsfiddle。

但事实上,他试图这样做并且没有使用已经加载到其他要求中的“主”模块,应证明他的方法有效。
如果定义外包给单个文件,那就像魅力一样。

感谢所有插话的人。

答案 4 :(得分:-2)

更优雅的解决方案是采用面向对象的方法,其中:

  1. 模块返回构造函数而不是实例,
  2. plugin可以是main
  3. 的子类

    这是实施(fiddle):

    console.clear();
    define("Main", [], function() {
        return function() { this.opt = "A"; };
    });
    define("Plugin", ["Main"], function(Main) {
        return function() {
            Main.call(this);
            this.opt = "B";
        };
    });
    require(["Plugin"], function(Plugin) {
        console.log((new Plugin()).opt); // should log B
    });
    require(["Main"], function(Main) {
        console.log((new Main()).opt); // should log A
    });