Home  >  Article  >  Web Front-end  >  Detailed interpretation of the entry function run in webpack

Detailed interpretation of the entry function run in webpack

亚连
亚连Original
2018-06-15 14:35:462168browse

Webpack is a front-end resource loading/packaging tool. It will perform static analysis based on module dependencies, and then generate corresponding static resources for these modules according to specified rules. This article mainly introduces the compile process of webpack source code - the entry function run. Friends who need it can refer to it

Webpack is currently the main packaging tool for applications developed based on React and Redux. I think many applications developed using Angular 2 or other frameworks also use Webpack.

The process of this section is as shown in the figure:

Now we have officially entered the packaging process. The starting method is run:

Compiler.prototype.run = (callback) => {
  const startTime = Date.now();
  const onCompiled = (err, compilation) => { /**/ };
  this.applyPluginsAsync("before-run", this, err => {
    if (err) return callback(err);
    this.applyPluginsAsync("run", this, err => {
      if (err) return callback(err);
      this.readRecords(err => {
        if (err) return callback(err);
        this.compile(onCompiled);
      });
    });
  });
}

Why not introduce the compiler object ? Because there is no initialization method in the constructor, it is just an ordinary variable declaration, there is nothing to talk about.

In the run method, tapable's applyPluginsAsync is first called to execute the before-run event stream. The event stream is defined as follows:

// NodeEnvironmentPlugin
compiler.plugin("before-run", (compiler, callback) => {
  if (compiler.inputFileSystem === inputFileSystem)
    inputFileSystem.purge();
  callback();
});

In the file system method of the compiler object In the mounted plug-in, the before-run event stream is injected. Here we first take a look at applyPluginsAsync (slightly modified to adapt to the webpack source code):

// tapable
Tapable.prototype.applyPluginsAsync = (name, ...args, callback) => {
  var plugins = this._plugins[name];
  if (!plugins || plugins.length === 0) return callback();
  var i = 0;
  var _this = this;
  // args为[args,next函数]
  args.push(copyProperties(callback, function next(err) {
    // 事件流出错或者全部执行完后调用回调函数
    if (err) return callback(err);
    i++;
    if (i >= plugins.length) {
      return callback();
    }
    // 执行下一个事件
    plugins[i].apply(_this, args);
  }));
  // 执行第一个事件
  plugins[0].apply(this, args);
};

At that time, this series of events was not mentioned in Section 8 Here is a brief explanation of the flow triggering method:

1. copyProperties is used to copy object properties, similar to Object.assign. However, two functions are passed in here, which are of no use at all! ! ! ! ! (I didn’t write an explanation at the time because I was stuck on what the object copy method is of use here)

2. In webpack, args is this, pointing to the context of the compiler

3. Events injected into this event stream must execute the callback method (as in the above example). At this time, it is not the external callback that is executed, but the next function

4. There are two situations where the external callback will be executed and an error occurs midway. Or all event streams have been executed

It will be very clear. The meaning of the function parameters injected into before-run is as follows:

// before-run
// compiler => this
// callback => next
(compiler, callback) => {
  if (compiler.inputFileSystem === inputFileSystem)
    inputFileSystem.purge();
  callback();
}

Since there is only one event in before-run, so before calling After the next method of the internal callback, the external callback will be called directly because i is greater than the event length.

The purge method here has been seen before. Let’s review the content here:

// NodeEnvironmentPlugin
compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000);
// CachedInputFileSystem
CachedInputFileSystem.prototype.purge = function(what) {
  this._statStorage.purge(what);
  this._readdirStorage.purge(what);
  this._readFileStorage.purge(what);
  this._readlinkStorage.purge(what);
  this._readJsonStorage.purge(what);
};
// CachedInputFileSystem => Storage
Storage.prototype.purge = function(what) {
  if (!what) {
    this.count = 0;
    clearInterval(this.interval);
    this.nextTick = null;
    this.data.clear();
    this.levels.forEach(function(level) {
      level.clear();
    });
  } else if (typeof what === "string") { /**/ } else { /**/ }
};

In one sentence, it can be summarized as: clear all cached data in the package.

Since it is assumed to be the first time, there is no actual operation here. Then the external callback is called and the run event stream is triggered in the same way.

The run event stream has only one method, which comes from the CachePlugin plug-in:

Compiler.plugin("run", (compiler, callback) => {
  // 这个属性我暂时也不知道是啥 反正直接callback了
  if (!compiler._lastCompilationFileDependencies) return callback();
  const fs = compiler.inputFileSystem;
  const fileTs = compiler.fileTimestamps = {};
  asyncLib.forEach(compiler._lastCompilationFileDependencies, (file, callback) => {
    // ...
  }, err => {
    // ...
  });
});

When the run event stream is triggered for the first time, that property is undefined, so it will be skipped directly, because I am I parsed it while looking at the source code, so I don’t know what it is, haha.

The next callback is this:

this.readRecords(err => {
  if (err) return callback(err);
  this.compile(onCompiled);
});

This is another prototype method, the source code is as follows:

Compiler.prototype.readRecords = (callback) => {
  // 这个属性也没有
  if (!this.recordsInputPath) {
    this.records = {};
    return callback();
  }
  this.inputFileSystem.stat(this.recordsInputPath, err => {
    // ...
  });
}

The first time here will also be skipped and the callback will be directed directly. Looking at the source code, you probably pass in a path and read the file information inside and cache it into records.

Now jump two steps, directly enter the prototype method compile, preview this function:

Compiler.prototype.compile = (callback) => {
  const params = this.newCompilationParams();
  // 依次触发事件流
  this.applyPluginsAsync("before-compile", params, err => {
    if (err) return callback(err);
    this.applyPlugins("compile", params);
    const compilation = this.newCompilation(params);
    this.applyPluginsParallel("make", compilation, err => {
      if (err) return callback(err);
      compilation.finish();
      compilation.seal(err => {
        if (err) return callback(err);
        this.applyPluginsAsync("after-compile", compilation, err => {
          if (err) return callback(err);
          return callback(null, compilation);
        });
      });
    });
  });
}

The core process of compilation and packaging has been clearly seen, and the method triggers before-compile, compile, make, after-compile event flow, and finally the callback function is called.

The above is what I compiled for everyone. I hope it will be helpful to everyone in the future.

Related articles:

How to implement a background video login page using Vue.js 2.0

How to use Vue to develop time conversion instructions ?

How to implement page adaptation in angularjs?

How to monitor window.resize in VueJs and how to implement it specifically?

Detailed interpretation of the concept of $window window object in AngularJS

How to implement React-native bridging to Android, and what are the specific steps?

The above is the detailed content of Detailed interpretation of the entry function run in webpack. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn