【问题标题】:Pattern for [lazy-loading] modules on demand按需 [延迟加载] 模块的模式
【发布时间】:2013-02-18 15:59:01
【问题描述】:

在我的应用程序中,我需要加载模块而不是在初始化阶段(通过在./modules/modules 中枚举它们),而是稍后根据某些条件(例如用户授权结果)按需加载。想象一下,我想为 User A 提供计算器模块,并为 User B 提供文本编辑器模块。

为了简单起见,我们以 boilerplatejs 示例应用为例,并假设 sampleModule1sampleModule2 将按需加载。

所以我从 src\modules\modules.js 中的初始加载序列中删除了模块:

    return [
        require('./baseModule/module'),
        /*require('./sampleModule1/module'),
        require('./sampleModule2/module')*/
    ];

并向摘要页面添加一个控件(src\modules\baseModule\landingPage\view.html)以按需加载它们:

<div>
    Congratulations! You are one step closer to creating your next large scale Javascript application!
</div>
<div>
    <select id="ModuleLoader">
        <option value="">Select module to load...</option>
        <option value="./modules/sampleModule1/module">sampleModule1</option>
        <option value="./modules/sampleModule2/module">sampleModule2</option>
    </select>
</div>

然后我修补 src\modules\baseModule\module.js 以将上下文传递给 LandingPageComponent(由于某种原因,它不在原始源代码中):

        controller.addRoutes({
            "/" : new LandingPageComponent(context)
        });

最后将加载代码添加到src\modules\baseModule\landingPage\component.js

            $('#ModuleLoader').on('change', function(){
                require([this.value], function(mod){
                    moduleContext.loadChildContexts([mod]);
                    alert('Module enabled. Use can now use it.');
                });
            });

这似乎有效,但这是最好的方法吗?

它是否正确处理上下文加载?

如何防止两次加载同一个模块?

【问题讨论】:

    标签: boilerplatejs


    【解决方案1】:

    聪明的想法在这里.. 过去几天我也在努力改进 BoilerplateJS 以延迟加载模块作为插件。我做了类似的事情,你可以在以下位置访问 POC 代码:

    https://github.com/hasith/boilerplatejs

    在我做的 POC 中,我试图同时实现“延迟加载”+“可配置 ui”。

    • 每个屏幕(称为应用程序)都是放置在布局上的组件(插件)的集合。这些应用程序定义只是一个 JSON 对象,要么从服务器 API 动态返回,要么静态定义为 JSON 文件(就像它在 POC 中一样)。在 POC 中,应用程序定义存储在“/server/application”中。

    • 现在可以按照约定动态调用这些应用程序。例如,“/##/shipping-app”将在“/server/application”中查找同名的应用程序定义。

    • 应用程序加载通过我在“/modules/baseModule/appShell”中的一个新组件进行。任何以 '/##/' 开头的内容都将被路由到该组件,然后它会尝试从“/server/application”文件夹加载应用程序定义按约定

    • 当“appShell”收到应用程序定义(作为 JSON)时,它也会尝试动态加载其中定义的组件(插件)。这些可插拔组件被放置在 'src/plugins' 中,也将按照约定加载。

    示例应用程序定义如下所示:

    {
        "application-id" : "2434",
        "application-name" : "Order Application",
        "application-layout" : "dummy-layout.css",
    
        "components" : [    
            {
                "comp-name" : "OrderDetails",
                "layout-position" : "top-middle"
            },
            {
                "comp-name" : "ShippingDetails",
                "layout-position" : "bottom-middle"
            }
        ]
    
    }
    

    该方法的好处如下:

    • 应用程序接口是组件驱动的,在运行时可配置
    • 可能会根据用户角色等(后端逻辑)向用户发送不同的应用程序定义
    • 可以通过组合现有组件即时创建应用程序
    • “框架”中无需更改静态代码即可添加新应用程序或组件,因为加载是基于约定的

    这些都是非常初步的想法。感谢任何反馈!

    【讨论】:

    • 我相信current implementation 会泄漏内存。我在 Shipping AppOrder App 之间切换之前和之后做了几次堆快照。它显示size increases,而每个插件都应该被理想地缓存和重用。可能这就是you asked yourself继续前进!
    • 哦,是的.. 这只是几个小时的 POC 来确认我的想法,在经过彻底测试之前,我不敢将它发布到 Boilerplate。我还计划将它构建到 Core 中,例如除了 Dom 和 Url 之外的“PluginController”之类的东西。
    【解决方案2】:

    您可以通过对更改事件使用命名函数并在执行后取消绑定函数来防止多次加载模块。

    【讨论】:

    • 我明白了,但我正在寻找一种更通用的方法来做到这一点,例如以某种方式访问​​父模块的上下文(或根上下文),并调用像 Context.LoadModule(moduleName) 这样可以处理所有复杂性的方法。
    猜你喜欢
    • 2018-05-22
    • 2017-02-22
    • 2017-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多