There are several solutions to this.
- Move script tags to the end of the body tag
asyncattribute to the script tag so it loads asynchronously
- Dynamically insert
These solutions solves the problem to a certain degree, but none of them fix the problems entirely. For example, solution 1 will not change overall page loading time. Solution 2 doesn't work in all browsers. Both 2 and 3 doesn't have control on the execution of the loaded scripts.
- Lazy loading; Load scripts only when needed
- Dependency management; Load dependencies recursively or load the bundles that contains all dependencies smartly
- Load asynchronously; Do not block other content or script in the page
- Works in all browsers
Let's take SystemJS as an example.
The essential way that SystemJS loads scripts is through Ajax or
XMLHttpRequest object. It wraps the request with a promise. And when the promise is fulfilled, it evaluates the script at the client side.
With promise it can load and execute the scripts sequentially without blocking. It can also load multiple scripts concurrently and know when the loading completes.
The benefit of using Ajax to load the scripts is that the scripts can be loaded as plain text and the client has the freedom to interpret the content.
For example, you can load ES modules in a non compliant browser using SystemJS then transpile it using babel to es5 syntax. (pre-requisite is to have babel standalone loaded in browser)
Babel has SystemJS plugin which transform all the
import...from... statement to
system.register(['dep1', 'dep2', ...], function ... .
This is essentially the AMD format. When used, it registers modules and dependencies in the SystemJS registry.
When loading a module with dependencies, it look up the path of each dependencies in the registry, if not found, it will try to resolve it to a url and load from that. This is recursively done. One way to optimize it is to use
depCache config variable to specify a flat array of dependencies.
Loading from the url is good since you can point it directly to node_modules folder. The downside is that the loaded module can only require/import modules that is recognizable by SystemJS.
Compiling code at server side is entirely a separate process. It can be done using a task runner like gulp or grunt. However when it's done it needs to hook back to SystemJS. For example, when bundling code, SystemJS needs to know what modules in which bundle. This is done in the SystemJS configuration.
I would suggest using SystemJS as a bundler for prototyping. The configuration can be really problematic for large projects as I can imagine.
Webpack is a much easier and more powerful alternative. Webpack does everything offline. It has pretty good defaults. It doesn't support loading URL as it overcomplicates things. The dependency loading is smooth and easy. The output of a built Webpack project is just JavsScripts. It's easy to include the files into a web page of our choice. The compilation pipeline is pluggable. You can plug in babel to transpile scripts depending on the extensions. It has built in support for tree shaking. For code using ES module syntax the final built size will be optimized.
Webpack also support dynamic import syntax. It smartly detect dynamic imports and create separate bundles for each one, so we can lazy load them.