【问题标题】:Requirejs and asynchronous & circular dependenciesRequirejs 和异步和循环依赖
【发布时间】:2014-05-11 00:46:37
【问题描述】:

我目前正在开发一个 Web 应用程序来编辑一些自定义文件格式(一个提供编辑配置文件界面的小编辑器)。 这个应用程序的一个关键元素是一个名为“FileReader”的对象,它是一个包装器,它根据检测到的配置文件类型触发正确的界面。这样我就生成了一个新的 FileReader( src ) - src 是一个 url 或一个 blob 并且它保持干净。

一旦检索到配置文件的类型,包装器就会检查它是否有与之关联的接口并简单地使用它。每个接口(针对每个不同的配置类型)都在一个单独的 JS 文件中定义。包装器完全加载后,我会挂钩所有事件并开始执行应用程序。

这是我与相关元素一起使用的 HTML 框架:

<!doctype html>
<html><head></head>
<body>
    <div id="UI"></div>
    <script src="js/fileReader/fileReader.js" >App.FileReader = function(){ ... }</script>
    <script src="js/fileReader/configType1.js" >App.FileReader.registerFileType("config1",object)</script>
    <script src="js/fileReader/configType2.js" >App.FileReader.registerFileType("config2",object)</script>
    <script src="js/fileReader/configType3.js" >App.FileReader.registerFileType("config3",object)</script>
    <script src="js/main.js" >App.run();</script>
</body>
</html>

现在,该应用长大了,我决定将我的应用转换为使用 requirejs。 我的问题主要是关于“什么是最好的组织”来处理这些模块,因为我只能认为一旦加载了所有文件类型模块,FileReader 就可以使用了,但我不能先加载它们,因为它们需要 mainWrapper注册他们的类型。

我能想到的最好的结构是:

main.js:

require(['require','FileReader','./fileReader/config1','./fileReader/config2','./fileReader/config3'],
    function( require ) {
        require(['require','./core/app'], function( require , App ) {
            App.run( window ) ;
        });
    });
});

fileReader.js:

define(function () {

    ...

    return FileReader ;

});

config1.js:

define(['FileReader'], function ( FileReader ) {

    var Interface = ... ;

    ...

    App.FileReader.registerFileType("config1",Interface)

    return FileReader ;

});

app.js:

define(['FileReader'], function ( FileReader ) {

    var App = function(){} ;

    App.run = function(){ FileReader.openFile( ... ) ; ... } ;

    return App ;

});

我的问题有什么意义? 为了确保 FileReader 对象包含正确的 Interface 对象,我首先强制 requirejs 手动加载所有 FileReader 相关文件,然后加载主应用程序文件。这样,一旦 App 对象请求 FileReader 对象,它就已经包含注册的正确接口。 我想要实现的只是请求 主文件中的“./core/app” 核心/应用文件中的“./fileReader” 然后在第一次加载时,如果它可以在 fileReader 文件中加载配置类型的模块,注册所有的东西,然后才返回答案。

我尝试的是:(在 FileReader 中)

fileReader.js:

define(["require"],function ( require ) {

    ...

    require(["config1","config2","config3"],
    function(){
        //occurs asynchronously, after the App.run() is triggered because of the synchronous return 
        callback(FileReader) ; //unfortunately I did not find any callback of this sort
                               //if it existed I could asynchronously find the list of the modules in the directory, load the them, and only then fire this fictive "ready" event
    }

    return FileReader ; //the first return occurs synchronously, so before the the fileReader is ready : modules are missing

});

那么加载此类模块的最佳方式是什么(在配置类型文件和主 FileReader 之间存在某种循环依赖)?如果我想强制它是异步的,那么我可以读取 config-type 目录中的可用内容怎么办?使用简单的 HTML 脚本列表,它们以正确的顺序加载,并且我可以轻松访问模块列表,现在我想确保在应用程序启动之前一切都准备就绪。

【问题讨论】:

    标签: javascript asynchronous requirejs circular-dependency appjs


    【解决方案1】:

    在我看来,您可以将记录可用文件类型的功能打包到一个新模块中,可能命名为FileTypeRegistry。所以你的配置文件可能是这样的:

    define(['FileTypeRegistry'], function ( FileTypeRegistry ) {
    
        var Interface = ... ;
    
        ...
    
        FileTypeRegistry.registerFileType("config1", Interface);    
    });
    

    这些模块不需要返回值。 registerFileType 只是在注册表中注册了一个类型。

    FileReader.js 可能包含:

    define(["FileTypeRegistry", "config1", "config2", "config3"], function ( FileTypeRegistry ) {
    
        var FileReader = {};
    
        var FileReader.openFile = function (...) {
            var impl = FileTypeRegister.getFileTypeImplementation(...);
        };
    
        return FileReader;
    });
    

    getFileTypeImplementation 根据类型名称检索实现(以前注册的 Interface)。

    【讨论】:

    • 我能够编辑 requirejs 源代码以更好地支持我所需要的,所以现在可以了。感谢您的回复:)
    【解决方案2】:

    Louis 的回答很有趣,但它只是解决了创建新模块的问题,即使它将依赖项放置在正确的上下文中,我也尝试搜索一些真正的异步模块定义(不仅仅是加载)。

    我最终能想到的最好办法是编辑 require.js 源文件以添加我预期的行为。我注册了一个名为“delay”的新处理程序(一种特殊的依赖行为,如“require”或“exports”),它为模块定义提供了一个回调函数:

    基本上,这是这样工作的:

    define(["delay"], function ( delay ) {
    
        var Module = ... ;
    
        setTimeout(function(){ //Some asynchronous actions
            delay(Module) ; //This does actually returns the Module's value and triggers the "defined" event.
                            //It's actually transparent so the other modules just "feel" that the network's load time was a bit longer.
        },1000);
    
        return Module ; //This does not do anything because delay is active
    
    });
    

    多亏了这个异步定义,我现在可以扫描目录(异步请求),然后透明地加载所有模块。 修改只代表大约 10 行,所以如果有人感兴趣,我把它贴在那里: https://github.com/jrburke/requirejs/pull/1078/files

    显然这不是异步导出的第一个请求,但它仍在争论中,所以我只是将它用于个人项目。

    【讨论】:

      猜你喜欢
      • 2015-03-27
      • 2017-10-27
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 2013-08-18
      • 1970-01-01
      • 2014-09-22
      • 2014-07-29
      相关资源
      最近更新 更多