[Javascript] Handwriting a webpack loder

[Javascript] Handwriting a webpack loder

Handwriting a loader

Why do I need a loader?

 Webpack can actually only process js files, so for other types of files besides js files, such as css sass, etc. . We can't handle it directly with webpack.

 We need a translator (loader) to help our files process. Sometimes we need more than one translator to work. For example, to translate classical Chinese into a foreign language, we must first convert it into vernacular and then into a foreign language.

 Loader is like a translator, it can output new results after the source file is transformed, and a file can be translated by multiple translators in a chain.

Take the scss file as an example:

 First hand over the scss source code to sass-loader to convert scss to css;
 submit the css output by sass-loader to css-loader for processing, find out the resources dependent on css, compress css;
 submit css output from css-loader It is processed by style-loader and converted into javascript code loaded by script.

 The final result must be javascript code.

Principles of writing loader

  Single responsibility: A loader only does one thing. Advantages: easy to maintain and can be chained.
  Modularity: to
ensure that the output is modular, the modules generated by the loader follow the same design principles as ordinary blocks and
  stateless: to
ensure that the loader does not save the state between different module conversions. Each run should be independent of other compiled modules and previous compilation results of the same module.

Write a basic loader

 Webpack runs on Node. A loader is actually a node module, which needs to export a function. The input of this exported function is the original content before processing, and the processed content is output after processing the content.

We now write a simple javascript loader, the role of this loader is to convert the string like in the content of the js file into ❤.

Directly on the code, we will name this loaderk-loader.js

module.exports = function (source) {
  const regExp = new RegExp("like", "ig")
  const result = source.replace(regExp, "❤")
  return result
}

How to debug our loader, we use the officially recommended one:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: path.resolve('path/to/loader.js'),
            options: {/* ... */}
          }
        ]
      }
    ]
  }
};

 Define the loader and find the path of the loader relative to the webpack configuration file.
 Let's see what k-loader.js has done, get the source, the source is the file content of the loaded js file, and then do a data cleaning, convert all the likes into ❤, and then return the string. That's it! That's ok, you can do a test, and you will find that all the likes in the js file have been replaced with ❤, is it very simple? . Yes.

Add some configurations to the basic loader

 Our general loader is configured with option, which means that users need to customize some functions.
We configure this like and ❤ in the option, so that it can be used flexibly, we only need to change the value of like and ❤. We use the auxiliary module provided loder-utilby webpack, and we can get the option value passed when webpack configures the loader through the getOPtions(this) method.
The code looks like this:

const loaderUtils = require("loader-utils")

module.exports = function (source) {
  
  const options = loaderUtils.getOptions(this) || {}
  // 假定option只有一个key value
  // 这里应该对option做一次验证。
  const key = Object.keys(options)[0]
  const value = options[key]

  const regExp = new RegExp(key, "ig")
  const result = source.replace(regExp, value)
  return result
}

We will value the options configured { like: "❤" }will be above us and the hard code to achieve the same effect, we will change the options {like: "love"}which would replace all js files inside like into love.

At this point, we can already write a loader by ourselves.


Return multiple values

 The loader we wrote above just returns one value. In some special scenarios, we need to use this.callback() to return multiple values.

 Taking babel-loader to convert ES6 code as an example, it needs to output the SourceMap corresponding to the converted ES5 code to facilitate the debugging of the source code. In order to return the Source Map to Webpack along with the ES5 code, you can also write:

module.exports = function(source) {
    this.callback(null, source, sourceMaps)
    return; // 当调用 callback() 时总是返回 undefined
}

This.callback  API looks like this:

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);

Synchronous or asynchronous

Loader is divided into synchronous and asynchronous, this time we need to use this.async()to get the callbackfunction.

module.exports = function(content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function(err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
};

reference:

Write a loader
loader-api

See the complete demo here:

https://github.com/aeolusheath/webpack-certain-features/tree/master/write-loader-diy

Good article should  pay attention to my  favorite article  

Guess you like

Origin blog.csdn.net/sd19871122/article/details/108227840