My question is why importing the Loaders in Webpack doesn't behave like the ways that Plugins do, and why Webpack design these two imports in different ways.
const CssLoader = require('css-loader')
...
use:[new CssLoader()]
CodePudding user response:
In short loaders are just function exported by modules that receive the source of a specific type of file and returns it to the next loader or Webpack compiler. And all those loader functions are called by Webpack loader-runner, So loader-runner needs a path of the exported function. || Most plugins allow configure behavior to initialize constructor is the best place. Plugins can be added without new keywords
Before anything what are the loader and plugins for Webpack?
Webpack can understand JavaScript and JSON files only. To handle files other than that loaders can be used. As Webpack treats everything (JavaScript, images, CSS, HTML...) as a module, the loader transform modules. babel-loader transforms ES6 to browser-compatible JavaScript. There are other loaders to with specific job. Like file-loader, URL-loader, CSS-loader, style-loader, and many more.
You can create your loader in just simple steps
process-loader.js
module.exports = function dummyLoader(source) {
console.log(source);
return source;
}
And add it modules.rule
to handle specific files.
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader', path.resolve(__dirname, './process-loader')]
}
]
}
Here each .js
file has to go through loaders. First, it will go through the babel-loader. babel-loader will transpile input source and return processed code. That processed code will be passed to the process-loader (exported module). It may process the source and return or return as it.
Loader is nothing but a function exported by JavaScript Module. And this function is called by Webpack loader-runner. Webpack loader-runner knows how to find loader from path, either it will look into node modules or other local file path. It receives source code as a parameter and returned it to the Webpack loader-runner. That returned code is passed down to the next loader. If you remove
return source
fromprocess-loader.js
you will end up with a white screen as the source is not returned and null passed to the next loader. The source from the last loader in the chain is passed down to the Webpack compiler for bundling. And due to this sequence of the loader is also essential.
In chained loaders Webpack calls loaders in reverse order i.e. from right to left.
For chained loader below
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: ["style-loader", "css-loader", "sass-loader",],
}
]
}
can be visualised like,
Why and how reverse, check Webpack Pitching Loaders
There is much more interesting about loaders you can find here
Plugins are nothing but Javascript objects that expose apply method. Plugin contributes to Compiler and Compilation. Plugins can hook into various life cycle phases of Webpack. The plugin can manage output with compiler hooks like beforeRun, run, watchRun, compile and many more or compilation hooks like optimizeAssets, processAssets.
The standard way of creating a plugin
const PLUGIN_NAME = 'DemoPlugin';
class DemoPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
}
}
module.exports = DemoPlugin;
And add to webpack. plugins
const DemoPlugin = require('./DemoPlugin');
...
plugins:[
new DemoPlugin({/*some config*/})
]
If you want to avoid new keywords, you can simply export the object with an apply method
module.exports = {
apply(compiler) {
compiler.hooks.watchRun.tap('WatchRun', (comp) => {
console.log('