Webpack loaders vs plugins; what's the difference?
Adding complementary and simpler answer.
Loaders:
Loaders work at the individual file level during or before the bundle is generated.
Plugins:
Plugins work at bundle or chunk level and usually work at the end of the bundle generation process. Plugins can also modify how the bundles themselves are created. Plugins have more powerful control than loaders.
Just for an example you can clearly see in below image where loaders are working and where plugins are working -
References: Article and Image
Loaders do the pre-processing transformation of virtually any file format when you use sth like require("my-loader!./my-awesome-module")
in your code. Compared to plugins, they are quite simple as they (a) expose only one single function to webpack and (b) are not able to influence the actual build process.
Plugins on the other hand can deeply integrate into webpack because they can register hooks within webpacks build system and access (and modify) the compiler, and how it works, as well as the compilation. Therefore, they are more powerful, but also harder to maintain.
At its core, webpack is just a file bundler. Considering a very simple scenario (no code splitting), this could mean just the following actions(on a high level):
- find the entry file and load its contents into memory
- match certain text within the content and evaluate those (for e.g. @import)
- find the dependencies based on previous evaluation and do the same with them
- stitch them all into a bundle in memory
- write the results to file system
When you examine the above steps closely, this resonates with what a Java compiler(or any compiler) does. There are differences of course but those don't matter to understand loaders and plugins.
Loaders:
are here because webpack promises to bundle together any file type.
Since webpack at its core is only capable enough to bundle js files, this promise meant that the webpack core team had to incorporate build flows which allowed external code to transform a particular file type in a way that webpack could consume.
These external code are called loaders and they typically run during step 1 and 3 above. Thus, since the stage at which these loaders need to run is obvious, they don't require hooks and neither do they influence the build process(since the build or bundle only happens at step 4).
So Loaders prepare the stage for compilation and they sort of extend the flexibility of the webpack compiler.
Plugins:
are here because even though webpack doesn't directly promise variable output, the world wants it and webpack does allow it.
Since webpack at its core is just a bundler and yet goes through several steps and sub-steps in doing so, these steps can be utilised to build in additional functionality.
The production build process(minifying and writing to file system), which is a native capability of webpack compiler, for e.g., can be treated as an extension of its core capability(which is just bundling) and can be treated like a native plugin. Had they not provided it, someone else would have done it.
Looking at the native plugin above, it appears as if the webpack bundling or compilation can be broken down into core bundling process, plus a lot of native plugin processes which we can turn off or customise or extend. This meant allowing external code to join in the bundling process at specific points that they can choose from (called hooks).
Plugins therefore influence the output and sort of extend the capability of webpack compiler.