Pattern for [lazy-loading] modules on demand

2019-08-03 04:52发布

问题:

In my application I need to load modules not at the initialization stage (by enumerating them in ./modules/modules), but on demand later based on some condition (e.g. user authorization results). Imagine I want to provide User A with calculator module, and User B with text editor module.

Let's take boilerplatejs example app for simplicity, and assume that sampleModule1 and sampleModule2 are to be loaded on demand.

So I remove the modules from initial loading sequence in src\modules\modules.js:

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

and add a control to summary page (src\modules\baseModule\landingPage\view.html) to load them on-demand:

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

Then I patch src\modules\baseModule\module.js to pass context to the LandingPageComponent (for some reason it doesn't in the original source code):

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

and finally add the loading code to 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.');
                });
            });

This appears to be working, but is this the best way to do it?

Does it handle context loading properly?

How to protect against loading the same module twice?

回答1:

Smart thinking here.. I was too working on improving BoilerplateJS to lazy-load modules as plugins last few days. I did something similar, and u can access the POC code at:

https://github.com/hasith/boilerplatejs

In the POC I did, I'm trying to achieve 'lazy loading' + 'configurable ui' at the same time.

  • Each of the screen (called an application) is a collection of components (plugins) placed on to a layout. These application definitions are just a JSON object either dynamically returned from server API or statically defined as JSON files (as it is in the POC). In the POC, application definitions are stored at "/server/application".

  • These applications can be called dynamically by convention now. For example "/##/shipping-app" will look for an application definition with the same name at "/server/application".

  • Application loading happens via a new component I have at '/modules/baseModule/appShell'. Anything staring with '/##/' will be routed to this component and then it will try to load the application definition by convention from "/server/application" folder.

  • When the 'appShell' receives the application definition (as a JSON), it will try to load the components (plugins) that are defined in it dynamically too. These pluggable components are placed in 'src/plugins' and will be loaded by convention too.

Sample application definition will look like below:

{
    "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"
        }
    ]

}

The benefit of the approach are as follows:

  • Applications interfaces are component driven and configurable at runtime
  • It is possible that different application definitions are sent to the user depending on the user role, etc (logic in backend)
  • Applications can be created on-the-fly by combining already existing components
  • There is no static code changes needed in the 'framework' either to add new applications or components since loading is based on convention

These are very initial thoughts around. Appreciate any feedback !



回答2:

You can protect against multiple loading of modules by using named functions for the change event, and unbinding the function after executing it.