About webpack

As a recent period of time limelight is full of packaging tools, Webpack basically occupied the front end of the circle. You’re embarrassed to say you don’t know webpack. Interested students can refer to my webpack introduction earlier. Indeed, the idea of webpack that everything is a module is really very convenient for our development. The function of packing CSS, images and other files can not be done without various Loaders. For a thing, we should know why and why. With this mentality, let’s take a look at loader’s relevant knowledge and how to develop it.

Learning methods

The best way to learn something new, I think, is its official documentation. For loader, take a look at its official documentation, you will know how to develop the simplest loader. However, its official document is in English, so I translated it to deepen my understanding. On the other hand, it provides a reference for other students. I’m sure you’ll know how to develop a Loader after reading the documentation.

What is the loader

Loader is a Node package that exposes a method. This method is called when some resource needs to be transformed.

Simple case

When there is only one loader to process a file, the loader is called with only one argument, which is the converted string of the content of the file.

The Loader can use this context to access the Laoder API during function execution for more efficient development.

A synchronous loader that requires only one value can simply return itself. In other cases, the loader can pass this. Callback (err, values…). Returns a series of values. Error is also passed to this.callback or thrown in the loader.

The loader expects to return 1 or 2 values, the first being the js code returned as a string or buffer after processing. The second is the SourceMap or JS object

Complications:

When multiple loaders are invoked chained, only the last loader gets the resource file. At the same time only the first loader is expected to return 1-2 values (namely JavaScript and SourceMap mentioned above). Other loaders receive values passed by the previous loader.

In other words, the chain loader executes from right to left or bottom up. For example: the following code is executed in bottom-up foo-loader==>bar-loader

module: {
  loaders: [{test: /\.js/.loaders: [
        'bar-loader'.'foo-loader']]}}Copy the code

Note: Currently Weboack will only search for the loader you specify under the Nodemodules folder

If your folder is not in that directory, you need to add a configuration under config to access node_modules by default. If your folder is not in that directory, you need to manually add it to the configuration file.

    resolveLoader: {
        modules: ['node_modules', path.resolve(__dirname, 'loaders')]}Copy the code

Warm prompt

Ps: After my own practice, I found that this is incorrect. I do not need to use path to resolve the problem. I can write the file directory directly. Generally speaking, loaders will be published to NPM for management. This situation is not to worry about, but if you want to test yourself during the development phase, you will face this situation. For example, MY hand-written MyLoader is under loaders, as shown below.

     resolveLoader:{
        modules: ['node_modules'.'loader']}Copy the code

Examples

It’s just a normal loader

    module.exports = function(source,map){
    this.cacheable && this.cacheable()
    this.value = source
    return '/*copy@ xiaoxiangdaiyu*/'+JSON.stringify(source)
    }Copy the code

Development of guidelines

The Loader must comply with the following requirements. The following items are listed in order of priority, with article 1 having the highest priority.

1. Single task

Loaders can be called chain-wise and do everything to create a loader for each step instead of a loader, that is, there is no need to convert them to JS if it is not necessary.

For example, convert a string template to HTML by querying a string. If you write a loader that does everything then you are violating the first requirement of loader. You should create a loader for each task and use them through pipes

  • Jade-loader: Converts a template to a module
  • Apply-loader: Creates a module and returns results by querying parameters
  • Html-loade: Creates a module that processes HTML and returns a string

Create a module for moulde, that is, a normal module

Modules generated by the loader should follow the same design principles as regular modules. For example, the following design is bad, not modularized, and depends on global state

    require("any-template-language-loader! ./xyz.atl");
    var html = anyTemplateLanguage.render("xyz");Copy the code

Try to indicate whether the loader can be cached

Most loaders are cacheable, so you should indicate whether they are cacheable. You only need to call it in loader

    // Cacheable identity loader
module.exports = function(source) {
    this.cacheable();
    return source;
};Copy the code

Do not save state between a run and a module

  • A Loader should be independent of other compiled modules. Unless it can handle these states on its own
  • A Loader should be independent of the compilation process prior to the same module.

Five, indicate dependence

If the Loader references other resources (such as file systems), they must be declared. This information is used to invalidate the cached loader and recompile it

    var path = require("path");
    module.exports = function(source) {
    this.cacheable();
    var callback = this.async();
    var headerPath = path.resolve("header.js");
    this.addDependency(headerPath);
    fs.readFile(headerPath, "utf-8".function(err, header) {
        if(err) return callback(err);
        callback(null, header + "\n" + source);
    });
};Copy the code

6. Resolve dependencies

Many languages provide specifications for declaring dependencies, such as @import and URL (…) in CSS. . These dependencies should be resolved by the module system.

Here are two solutions:
  • 1. Convert them to require
  • Resolve the path using the this.resolve method
Here are two examples
  • 1, csS-loader: convert dependencies to require, which replaces @import with url(…) Resolves dependencies on other style files
  • 2. Less-loader: It cannot be done like CSS-loader because all less files need to be compiled together to resolve variables and mixins. So it extends the less compilation process with a common path logic. This common logic uses this.resolve to resolve files with module system configuration items. Aliasing, Custom Module directories, etc.

If the language only accepts relative urls (as in CSS where URL (file) always stands for./file), use ~ to indicate a module dependency.

    url(file) -> require("./file")
    url(~module) - >require("module")Copy the code

7. Remove common code

Extract Common Code I feel better translated into the title above. In fact, all languages follow this idea, that is, don’t write a lot of code that is used by each module, create a Runtime file in the Loader, put the common code in it

Avoid writing absolute paths

Do not write absolute paths into module code. They will break the hash process when the root of the project changes. The stringifyRequest method of loader-utils should be used to convert absolute paths to relative paths. Example:

    var loaderUtils = require("loader-utils");
    return "var runtime = require(" +
    loaderUtils.stringifyRequest(this."!" + require.resolve("module/runtime")) +
  ");";Copy the code

Use peerDependencies to specify libraries to rely on

Using peerDependency allows application developers to specify versions of dependencies in package.json. These dependencies should be relatively open to allow the library to be upgraded without redistributing the Loader version. In short, libraries that are dependent on peerDependency should be loosely coupled so that there is no need to re-change the Loader version when the library version changes.

10. Programmable objects as query items

In some cases, the Loader needs some programmable object that cannot be parsed by the method as a serialized Query parameter. Less-loader, for example, provides this possibility through a specific less-plugin. In this case, the Loader should allow you to extend the Options object of the Webpack to get the specific option. To avoid name collisions, it is necessary to name based on the Loader namespace.

     // webpack.config.js
    module.exports = {
        ...
    lessLoader: {
        lessPlugins: [
        new LessPluginCleanCSS({advanced: true}}})];Copy the code

conclusion

So far, how to develop a Webpack loader I believe we already know, if not too clear, you can move to w-loader view. Besides, it’s really difficult for me to translate. Here, I hope we can discuss and learn together.