I have a module composition like this:
angular.module('mainModule',["cityModule", "countryModule"]);
angular.module('mapModule',[]);
angular.module('cityModule',["mapModule"]);
angular.module('countryModule',["mapModule"]);
main module contains countryModule and cityModule. And mapModule goes to cityModule and countryModule.
so I have a config on mapModule.
angular.module("mapModule").config([function () {
console.log("this is map config")
}]);
I have seen that writes "this is map config" only one time. But I referenced mapModule two different modules (cityModule and countryModule).
Should it write two times? Why?
(if I have a provider on mapModule it writes console one times as well.)
The single output is correct.
From the documentation:
Modules can list other modules as their dependencies. Depending on a
module implies that the required module needs to be loaded before the
requiring module is loaded. In other words the configuration blocks of
the required modules execute before the configuration blocks of the
requiring module. The same is true for the run blocks. Each module can
only be loaded once, even if multiple other modules require it.
In your example mapModule's config will run first, but only once.
Related
I'm trying to build a web application made one main entry point (index.js) and a number of modules that I want to load dynamically.
Something like:
index.js
document.getElementById('button1').addEventListener('click', async function(){
const mod = await import('module1.js');
mod.run();
});
module1.js
export function run() {
console.log("Hello from module1");
}
Is it possible to build a bundle for each of these files using Webpack 5?
What I tried
output.library (but it builds everything as libraries)
It seems that Webpack lets me set multiple entry points.
However to expose all of module1.js's exports I need to set output.library.
But if I do, then index.js is built as a library too, since there can be only one output field.
Multiple configurations (but it breaks the dev server)
I could configure Webpack to build both my main script and the modules using multiple configurations, but if I try this, then the webpack-dev-server tries to start two instances and fails.
It prints errors like:
<e> [webpack-dev-middleware] ConcurrentCompilationError: You ran Webpack twice. Each instance only supports a single concurrent compilation at a time.
What else can I try?
Found an answer: the correct way to do this is to use a dynamic import. By default Webpack places the dynamically-imported resource in different files.
More documentation:
Code Splitting
Lazy Loading
Dynamic expressions in import()
I'm working on a legacy app in durandal. I noticed when I define a module:
define({
message: 'hello world'
});
Then the output from grunt looks like:
define('filePath\fileName',{
message: 'hello world'
});
Where does filePath\fileName come from? I tried building a module in plain RequireJS and I had to supply this id myself. I read that Durandal uses a pattern called AMD (Asynchronous Module Definition pattern). Is the filePath\fileName using fileName.js convention a part of the AMD pattern?
AMD optimizers add these ids when they optimize your modules.
As long as you have one module per file, an AMD loader is able to infer the module id from the path of the file and from the configuration of the module loader. So it is okay when you have one module per file to omit the module id from the define call, and let the AMD loader infer the module id. This is called an "anonymous define, by the way.
One of the jobs of the optimizer is to combine multiple modules in a single file. When the optimizer combines multiple modules in the same file, then there's no way to distinguish which define call belongs to which module, unless the optimizer also records this information. The way the AMD specification is designed, the way to record which define belongs to which module is to add the module id as the first argument of define.
Final Edit
The tl;dr resolution of this is that it's impossible. Though the top answer below does have some good information.
Consider the code below, from contacts.js. This is a dynamically loaded module, loaded on demand with System.import elsewhere in the code.
If SharedUtil1 is also used in other modules which are also dynamically loaded with System.import, how would I go about having SharedUtility1 excluded from all of these modules, and only loaded on demand the first time it's needed?
A top-level System.import of SharedUtil1 won't work, since my export depends on it: exports can only be placed in the top level of a module's code, not in any sort of callback.
Is this possible with Webpack? I'm on version 2.0.7 beta.
import SharedUtil1 from '../../SharedUtilities/SharedUtility1';
class Contacts{
constructor(data){
this.data = data;
this.sharedUtil1 = new SharedUtil1();
}
}
export default Contacts;
UPDATE 1
I thought the bundle loader was what I wanted, but no, that turns your imported module into a different function that you call with a callback to get to the actual module, once it's done loading asynchronously. This means you can't transparently make module X load asynchronously without making breaking changes to your code, to say nothing of the fact that you're back to the problem originally described, that if your top-level module depends on the now-asynchronously loaded dependency, there's no way to export it, since exports must be at the top level.
Is there no way in Webpack to denote that dependency X is to be loaded on-demand, if needed, and have any imported modules which import it to transparently wait out the importation process? I would think this use case would be a sine qua non for any remotely large application, so I have to think I'm just missing something.
UPDATE 2
Per Peter's answer, I attempted to get deduplication working, since the commonChunk plugin relates to sharing code between end points, as he mentioned, and since require.ensure places the loaded code into a callback, thereby preventing you from ES6 exporting any code that depends on it.
As far as deduplication, contacts.js and tasks.js both load the same sharedUtil like so
import SharedUtil1 from '../../sharedUtilities/sharedUtility1';
I tried running webpack as
webpack --optimize-dedupe
and also by adding
plugins: [
new webpack.optimize.DedupePlugin()
]
to webpack.config. In both cases though the sharedUtil code is still placed in both the contacts and tasks bundles.
After reading your blog post I finally understand what you intended. I got a bit confused by the word "Top-level dependencies".
You have two modules (async-a and async-b) which are loaded on-demand from anywhere (here a module main) and both have a reference on a shared module (shared).
- - -> on-demand-loading (i. e. System.import)
---> sync loading (i. e. import)
main - - -> async-a ---> shared
main - - -> async-b ---> shared
By default webpack creates a chunk tree like this:
---> chunk uses other chunk (child-parent-relationship)
entry chunk [main] ---> on-demand chunk 1 [async-a, shared]
entry chunk [main] ---> on-demand chunk 2 [async-b, shared]
This is fine when shared < async-a/b or the probability that async-a and async-b are used both by the same user is low. It's the default because it's the simplest behaviors and probably what you would expect: one System.import => one chunk. In my opinion it's also the most common case.
But if shared >= async-a/b and the probability that async-a and async-b is loaded by the user is high, there is a more efficient chunking option: (a bit difficult to visualize):
entry chunk [main] ---> on-demand chunk 1 [async-a]
entry chunk [main] ---> on-demand chunk 2 [async-b]
entry chunk [main] ---> on-demand chunk 3 [shared]
When main requests async-a: chunk 1 and 3 is loaded in parallel
When main requests async-b: chunk 2 and 3 is loaded in parallel
(chunks are only loaded if not already loaded)
This is not the default behavior, but there is a plugin to archive it: The CommonChunkPlugin in async mode. It find the common/shared modules in a bunch of chunks and creates a new chunks which includes the shared modules. In async mode it does load the new chunk in parallel to the original (but now smaller) chunks.
new CommonsChunkPlugin({
async: true
})
// This does: (pseudo code)
foreach chunk in application.chunks
var shared = getSharedModules(chunks: chunk.children, options)
if shared.length > 0
var commonsChunk = new Chunk(modules: shared, parent: chunk)
foreach child in chunk.children where child.containsAny(shared)
child.removeAll(shared)
foreach dependency in chunk.getAsyncDepenendenciesTo(child)
dependeny.addChunk(commonsChunk)
Keep in mind that the CommonsChunkPlugin has a minChunks option to define when a module is threaded as shared (feel free to provide a custom function to select the modules).
Here is an example which explains the setup and output in detail: https://github.com/webpack/webpack/tree/master/examples/extra-async-chunk
And another one with more configuration: https://github.com/webpack/webpack/tree/master/examples/extra-async-chunk-advanced
If I've understood you correctly, you want to prevent the same dependency being loaded multiple times when different code chunks declare it as a dependency.
Yes this is possible; how to do it depends on both context in your application and whether it is in ES6 or ES5.
ECMA Script 5
Webpack 1 was built in ECMA Script 5 and typically uses either CommonJS or RequireJS syntax for module exporting and importing. When using this syntax, the following features can be used to prevent duplicate code:
Deduplication prevents duplicate files being included in the compiled
code by creating copies of the duplciate functions instead of
redefining them.
Named Chunks allows chunks to be declared as dependencies but not immediately evaluated; all occurrences of the same chunk will use the same instance.
CommonsChunkPlugin allows a chunk to be shared across multiple entry points (only applies to multiple page websites).
Deduplication
From the webpack documentation:
If you use some libraries with cool dependency trees, it may occur
that some files are identical. Webpack can find these files and
deduplicate them. This prevents the inclusion of duplicate code into
your bundle and instead applies a copy of the function at runtime. It
doesn’t affect semantics.
emphasis is mine, not from source
As described by the documentation, the code splitting remains unchanged; each module that needs sharedUtil1 should declare the require as normal. To prevent the same dependency being loaded multiple times, a webpack setting is enabled that causes webpack to explicitly check files for duplication before including them at runtime.
This option is enabled with
--optimize-dedupe resp. new webpack.optimize.DedupePlugin()
Named Chunks
From the webpack documentation:
The require.ensure function accepts an additional 3rd parameter. This
must be a string. If two split point pass the same string they use the
same chunk...
require.include can be useful if a module is in multiple child chunks.
A require.include in the parent would include the module and the
instances of the modules in the child chunks would disappear.
In short, the loading of the modules is delayed until later in the compiling. This allows duplicate definitions to be stripped before they are included. The documentation provides examples.
Common Chunk Plugin
From the webpack documentation:
The CommonsChunkPlugin can move modules that occur in multiple entry
chunks to a new entry chunk (the commons chunk). The runtime is moved
to the commons chunk too. This means the old entry chunks are initial
chunks now.
This is very specific to sharing chunks between multiple pages, it is not relevant in other circumstances.
ECMA Script 6
Support for advanced module import features is... a work in progress. To get a feel for where things are, see the following links:
Ongoing What's new in Webpack 2
2015/12/20 Tree-Shaking
Ongoing Webpack 2 Roadmap
Here's a good summary of ES6 modules and webpack: ES6 Modules with TypeScript and Webpack
The above information is likely to become out-of-date fast.
Suggestion
For your own sanity, I suggest:
If optimisation matters:
Revert to CommonJS / RequireJS syntax and upgrade to ECMA Script 6 when Webpack 2 is stable and released.
If ECMA Script 6 syntax matters:
Use the standard ECMA Script 6 import export format and add optimisation features as they become available.
There is simply too much flux to try and use advanced module loading features in the sill unstable webpack 2. Wait for things to settle down and for some really good plugins to become available before even attempting it.
Per the Webpack creator, this is impossible. Plain and simple. See Peter's answer for plenty of other good info regarding Webpack and ES6.
The pasted image was the result of a misunderstanding. See the same user's answer above.
System.import has been deprecated in Webpack. Webpack now favors import() which requires a polyfill for promises.
Code Splitting - Using import()
I am using RequireJS to modularize my code. The website I am using will have several distinct page categories. For each one of them I want to load general JS file with all the dependencies that exist across all page categories, jQuery being most obvious one. I have managed to do that already.
For the rest of the pages, I want to have separate JS files. Each page has its own module but I am thinking that it would make more sense if I download several pages modules all at once and this way when the user comes to other pages, he will have the JS file already cached and won't have to download.
For now I tried to concatenate these modules which did work rather well. However, I am not sure how to load the specific module from a file. So far I am doing this:
require(['./js/common'], function (common) {
require(['public']);
});
common is the dependencies file and public is my concatenated file with all the dependencies. Inside it has modules for each page but I don't know how to call a specific one. Any ideas?
Take a look at the bundles option for RequireJS' runtime configuration. In your case it would be something like:
bundles: {
public: ['mod1', 'mod2', ...]
}
I've used mod1, mod2 because I don't see the name of the actual modules in your question but what you'd want there are the names of the modules inside the public bundle and that you want to load individually. With this, RequireJS will know that when you want to load such module, it has to get them from public instead of searching for them indivdually.
Then you should be able to change your loading code to:
require(['./js/common'], function (common) {
require(['mod1']);
});
Again, mod1 is for illustration.
Take a look at require-lazy.
It allows you to bundle independent parts of the application into separate bundles. From the developer's point of view, instead of:
define(["module1", "module2"], function(module1, module2) {...});
you write:
define(["lazy!module1", "lazy!module2"], function(module1, module2) {...});
Modules 1 & 2 will not be loaded upfront; they and their dependencies will be loaded lazilly when you call:
module1.get().then(function(realModule1) {
// here you have access to the real module1
});
Check out the examples for a few use cases.
Using requirejs, I have two files that depend on the same module.
Must I make sure that I always use the same module name in both dependencies?
For example, if I defined a path for require.config:
'myMod':'src/js/myModule'
And then one time use the name and one time use the actual path, like so:
//in a.js
define(function(require){
var mod = require('myMod');
});
//in b.js
define(function(require){
var mod = require('src/js/myModule');
});
Obviously, the best practice would be to use the module name everywhere, but I want to know why this causes an actual error.
Does requirejs store a map between the module name used and the actual loaded module?
As you yourself say:
Obviously, the best practice would be to use the module name everywhere
RequireJS enforces this practice, up to a point. The thing is that when you tell RequireJS in your paths that 'myMod':'src/js/myModule' you are effectively saying that you want to load src/js/myModule under the name myMod. In all likelihood, if you then refer to it by the full path this is an error in your part. If this were to happen in my code, I'd consider it to be an error. So I'd like RequireJS to fail when this happens.
There's also the issue of semantics. The basic case is that if two names passed to require differ, once relative paths have been resolved, then they refer to two different modules. Going by this, if you do require('myMod') and require('src/js/myModule') you are expecting two different modules. And if you do require('myMod') and require('myMod'), you get the same module. So with the paths you've set, what do you actually want? The same module or different modules?
RequireJS has ways to give you both. If you want both module names to give you the same module, then you remove your paths config and use a map config instead:
map: {
'*': {
myMod: 'src/js/myModule'
}
}
What this means is "in all modules (*), if a request is made for the module myMod, then load module src/js/myModule instead." The intent is crystal clear.
If you'd like instead both paths to give you different modules although they share the same source, you could do it using a RequireJS plugin that loads the same source for your modules but has RequireJS register this single source as different modules.