前面已经讲到Compile是继承自Tapable的。
在我们使用Webpack-dev-server(基于express)创建开发环境服务器的时候我们有使用到中间件

// node_modules/webpack-dev-server/lib/Server.js
this.middleware = webpackDevMiddleware(
      compiler,
      Object.assign({}, options, wdmOptions)
);
// node_modules/webpack-dev-middleware/index.js
module.exports = function wdm(compiler, opts) {
  const options = Object.assign({}, defaults, opts);

  if (options.lazy) {
    if (typeof options.filename === 'string') {
      const filename = options.filename
        .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') // eslint-disable-line no-useless-escape
        .replace(/\\\[[a-z]+\\\]/gi, '.+');

      options.filename = new RegExp(`^[/]{0,1}${filename}$`);
    }
  }

  // defining custom MIME type
  if (options.mimeTypes) {
    const typeMap = options.mimeTypes.typeMap || options.mimeTypes;
    const force = !!options.mimeTypes.force;
    mime.define(typeMap, force);
  }

  const context = createContext(compiler, options);

  // start watching
  if (!options.lazy) {
    context.watching = compiler.watch(options.watchOptions, (err) => {
      if (err) {
        context.log.error(err.stack || err);
        if (err.details) {
          context.log.error(err.details);
        }
      }
    });
  } else {
    context.state = true;
  }
    if (options.writeToDisk) {
    toDisk(context);
  }

  setFs(context, compiler);

  return Object.assign(middleware(context), {})
}

在开发过程中,我们会执行compile.watch()

// node_modules/webpack/lib/Compiler.js
watch(watchOptions, handler) {
    if (this.running) return handler(new ConcurrentCompilationError());

    this.running = true;
    this.watchMode = true;
    this.fileTimestamps = new Map();
    this.contextTimestamps = new Map();
    this.removedFiles = new Set();
    return new Watching(this, watchOptions, handler);
}

上面会返回一个Watching的实例
在它的构造函数里面会有个记录文件变化的回调

// node_modules/webpack/lib/Watching.js
class Watching {
    constructor(compiler, watchOptions, handler) {
        this.startTime = null;
        this.invalid = false;
        this.handler = handler;
        this.callbacks = [];
        this.closed = false;
        if (typeof watchOptions === "number") {
            this.watchOptions = {
                aggregateTimeout: watchOptions
            };
        } else if (watchOptions && typeof watchOptions === "object") {
            this.watchOptions = Object.assign({}, watchOptions);
        } else {
            this.watchOptions = {};
        }
        this.watchOptions.aggregateTimeout =
            this.watchOptions.aggregateTimeout || 200;
        this.compiler = compiler;
        this.running = true;
        this.compiler.readRecords(err => {
            if (err) return this._done(err););
            this._go();
        });
    }
    _go() {
        this.startTime = Date.now();
        this.running = true;
        this.invalid = false;
        this.compiler.hooks.watchRun.callAsync(this.compiler, err => {
            if (err) return this._done(err);
            const onCompiled = (err, compilation) => {
                if (err) return this._done(err);
                if (this.invalid) return this._done();

                if (this.compiler.hooks.shouldEmit.call(compilation) === false) {
                    return this._done(null, compilation);
                }

                this.compiler.emitAssets(compilation, err => {
                    if (err) return this._done(err);
                    if (this.invalid) return this._done();
                    this.compiler.emitRecords(err => {
                        if (err) return this._done(err);

                        if (compilation.hooks.needAdditionalPass.call()) {
                            compilation.needAdditionalPass = true;

                            const stats = new Stats(compilation);
                            stats.startTime = this.startTime;
                            stats.endTime = Date.now();
                            this.compiler.hooks.done.callAsync(stats, err => {
                                if (err) return this._done(err);

                                this.compiler.hooks.additionalPass.callAsync(err => {
                                    if (err) return this._done(err);
                                    this.compiler.compile(onCompiled);
                                });
                            });
                            return;
                        }
                        return this._done(null, compilation);
                    });
                });
            };
            this.compiler.compile(onCompiled);
        });
    }
}

每执行一次就是执行this.compiler.hooks.watchRun.callAsync的回调了,并在回调里面调用this.compiler.compile(onCompiled);,然后还会执行this.compiler.hooks.shouldEmit.callthis.compiler.emitAssetscompilation.hooks.needAdditionalPass.callthis.compiler.hooks.done.callAsyncthis.compiler.hooks.additionalPass.callAsync,最后在this.compiler.hooks.additionalPass.callAsync的回调里面还会执行this.compiler.compile(onCompiled);。。。各种回调。
this.compiler.hooks.watchRun.callAsync回调里面我们还会执行this._done()

_done(err, compilation) {
    this.running = false;
    if (this.invalid) return this._go();

    const stats = compilation ? this._getStats(compilation) : null;
    if (err) {
        this.compiler.hooks.failed.call(err);
        this.handler(err, stats);
        return;
    }
    this.compiler.hooks.done.callAsync(stats, () => {
        this.handler(null, stats);
        if (!this.closed) {
            this.watch(
                Array.from(compilation.fileDependencies),
                Array.from(compilation.contextDependencies),
                Array.from(compilation.missingDependencies)
            );
        }
        for (const cb of this.callbacks) cb();
        this.callbacks.length = 0;
    });
}

根据是否出错来分别调用this.compiler.hooks.failed.call或者this.compiler.hooks.done.callAsync
Compiler的compile方法是个比较重要的方法。

// node_modules/webpack/lib/Compiler.js
compile(callback) {
    const params = this.newCompilationParams();
    this.hooks.beforeCompile.callAsync(params, err => {
        if (err) return callback(err);

        this.hooks.compile.call(params); // 调用compile钩子

        const compilation = this.newCompilation(params); // 生成编译器

        this.hooks.make.callAsync(compilation, err => {
            if (err) return callback(err);

            compilation.finish(); // 没有错误的话,编译结束会调用compilation的finishModules这个钩子;

            // 会调用compilation的不少钩子
            compilation.seal(err => {
                if (err) return callback(err);

                this.hooks.afterCompile.callAsync(compilation, err => { // 执行编译成功的钩子
                    if (err) return callback(err);

                    return callback(null, compilation);
                });
            });
        });
    });
}

先生成编译的参数,然后执行this.hooks.beforeCompile.callAsync在对应的回调里面
先生成编译器Compilationconst compilation = this.newCompilation(params)

分类: Web前端

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据