webpack-cli

执行过程

  1. 1.webpack.config.js,shell options参数解析
  2. 2.new webpack(options)
  3. 3.run() 编译的入口方法
  4. 4.compile() 出发make事件
  5. 5.addEntry() 找到js文件,进行下一步模块绑定
  6. 6._addModuleChain() 解析js入口文件,创建模块
  7. 7.buildModule() 编译模块,loader处理与acorn处理AST语法树
  8. 8.seal() 每一个chunk对应一个入口文件
  9. 9.createChunkAssets() 生成资源文件
  10. 10.MainTemplate.render() __webpack__require()引入
  11. 11.ModuleTemplate.render() 生成模版
  12. 12.module.source() 将生成好的js保存在compilation.assets
  13. 13.Compiler.emitAssets()通过emitAssets将最终的js输出到outputpath

参数解析

  1. (function(){
  2. yargs.options({...})
  3. yargs.parse(process.argv.slice(2), (err, argv, output) => {...})
  4. })()

加载webpack.config.js

  1. (function(){
  2. ...
  3. yargs.parse(process.argv.slice(2), (err, argv, output) => {
  4. ...
  5. //解析argv,拿到配置文件参数
  6. let options = require("./convert-argv")(argv);
  7. function processOptions(options){
  8. ...
  9. }
  10. processOptions(options);
  11. })
  12. })()

执行webpack()

  1. (function(){
  2. ...
  3. yargs.parse(process.argv.slice(2), (err, argv, output) => {
  4. ...
  5. //解析argv,拿到配置文件参数
  6. let options = require("./convert-argv")(argv);
  7. function processOptions(options){
  8. ...
  9. const webpack = require("webpack");
  10. compiler = webpack(options);
  11. }
  12. processOptions(options);
  13. })
  14. })()

webpack.js

  1. const webpack = (options, callback) => {
  2. //验证webpack.config.js合法性
  3. const webpackOptionsValidationErrors = validateSchema(
  4. webpackOptionsSchema,
  5. options
  6. );
  7. /*
  8. [
  9. { entry: './index1.js', output: { filename: 'bundle1.js' } },
  10. { entry: './index2.js', output: { filename: 'bundle2.js' } }
  11. ]
  12. */
  13. if (Array.isArray(options)) {
  14. compiler = new MultiCompiler(options.map(options => webpack(options)));
  15. } else if(typeof options === "object"){
  16. ...
  17. //创建一个comiler对象
  18. compiler = new Compiler(options.context);
  19. //往comiler中注册插件
  20. new NodeEnvironmentPlugin().apply(compiler);
  21. //执行config中配置的插件
  22. if (options.plugins && Array.isArray(options.plugins)) {
  23. for (const plugin of options.plugins) {
  24. if (typeof plugin === "function") {
  25. plugin.call(compiler, compiler);
  26. } else {
  27. plugin.apply(compiler);
  28. }
  29. }
  30. }
  31. //执行插件environment生命周期钩子方法
  32. compiler.hooks.environment.call();
  33. compiler.hooks.afterEnvironment.call();
  34. //执行webpack内置插件
  35. compiler.options = new
  36. WebpackOptionsApply().process(options, compiler);
  37. }else {
  38. throw new Error("Invalid argument: options");
  39. }
  40. if (callback) {
  41. ...
  42. //调用compiler.run开始编译
  43. compiler.run(callback);
  44. }
  45. //将compiler对象返回
  46. return compiler
  47. }
  1. //NodeEnvironmentPlugin.js
  2. class NodeEnvironmentPlugin {
  3. apply(compiler) {
  4. ...
  5. compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {
  6. if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge();
  7. });
  8. }
  9. }
  10. module.exports = NodeEnvironmentPlugin;
  1. class WebpackOptionsApply extends OptionsApply {
  2. constructor() {
  3. super();
  4. }
  5. process(options, compiler) {
  6. //挂载配置,执行插件
  7. let ExternalsPlugin;
  8. compiler.outputPath = options.output.path;
  9. compiler.recordsInputPath = options.recordsInputPath || options.recordsPath;
  10. compiler.recordsOutputPath =
  11. options.recordsOutputPath || options.recordsPath;
  12. compiler.name = options.name;
  13. new EntryOptionPlugin().apply(compiler);
  14. new HarmonyModulesPlugin(options.module).apply(compiler);
  15. new LoaderPlugin().apply(compiler);
  16. ...
  17. }
  18. }
  19. module.exports = WebpackOptionsApply;

compiler.run() 开始编译

  1. class Compiler extends Tapable{
  2. constructor(context){
  3. ...
  4. }
  5. watch(){...}
  6. run(callback){
  7. ...
  8. const onCompiled = (err, compilation){
  9. ...
  10. }
  11. //执行生命周期钩子
  12. this.hooks.beforeRun.callAsync(this, err => {
  13. ...
  14. this.hooks.run.callAsync(this, err =>{
  15. this.readRecords(err =>{
  16. ...
  17. //开始编译
  18. this.compile(onCompiled);
  19. })
  20. }
  21. }
  22. }
  23. compile(callback) {
  24. //拿到参数
  25. const params = this.newCompilationParams();
  26. //执行编译前钩子
  27. this.hooks.beforeCompile.callAsync(params, err => {
  28. ...
  29. //创建compilation对象
  30. const compilation = this.newCompilation(params);
  31. //开始构建模块对象
  32. this.hooks.make.callAsync(compilation, err =>{
  33. })
  34. }
  35. }
  36. createCompilation() {
  37. //创建comilation对象
  38. return new Compilation(this);
  39. }
  40. newCompilation(params) {
  41. //调用创建compilation对象方法
  42. const compilation = this.createCompilation();
  43. }
  44. }
  45. module.exports = Compiler;

创建 Compilation()

  1. class Compilation extends Tapable {
  2. constructor(compiler) {
  3. super();
  4. ...
  5. //初始化配置
  6. this.compiler = compiler;
  7. this.resolverFactory = compiler.resolverFactory;
  8. this.inputFileSystem = compiler.inputFileSystem;
  9. this.requestShortener = compiler.requestShortener;
  10. //初始化模版
  11. this.mainTemplate = new MainTemplate(this.outputOptions);
  12. this.chunkTemplate = new ChunkTemplate(this.outputOptions);
  13. this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
  14. this.outputOptions
  15. );
  16. }
  17. }
  1. class MainTemplate extends Tapable {
  2. this.hooks.requireExtensions.tap("MainTemplate", (source, chunk, hash) => {
  3. const buf = [];
  4. const chunkMaps = chunk.getChunkMaps();
  5. // Check if there are non initial chunks which need to be imported using require-ensure
  6. if (Object.keys(chunkMaps.hash).length) {
  7. buf.push("// This file contains only the entry chunk.");
  8. buf.push("// The chunk loading function for additional chunks");
  9. buf.push(`${this.requireFn}.e = function requireEnsure(chunkId) {`);
  10. buf.push(Template.indent("var promises = [];"));
  11. buf.push(
  12. Template.indent(
  13. this.hooks.requireEnsure.call("", chunk, hash, "chunkId")
  14. )
  15. );
  16. buf.push(Template.indent("return Promise.all(promises);"));
  17. buf.push("};");
  18. } else if (
  19. chunk.hasModuleInGraph(m =>
  20. m.blocks.some(b => b.chunkGroup && b.chunkGroup.chunks.length > 0)
  21. )
  22. ) {
  23. // There async blocks in the graph, so we need to add an empty requireEnsure
  24. // function anyway. This can happen with multiple entrypoints.
  25. buf.push("// The chunk loading function for additional chunks");
  26. buf.push("// Since all referenced chunks are already included");
  27. buf.push("// in this file, this function is empty here.");
  28. buf.push(`${this.requireFn}.e = function requireEnsure() {`);
  29. buf.push(Template.indent("return Promise.resolve();"));
  30. buf.push("};");
  31. }
  32. buf.push("");
  33. buf.push("// expose the modules object (__webpack_modules__)");
  34. buf.push(`${this.requireFn}.m = modules;`);
  35. buf.push("");
  36. buf.push("// expose the module cache");
  37. buf.push(`${this.requireFn}.c = installedModules;`);
  38. buf.push("");
  39. buf.push("// define getter function for harmony exports");
  40. buf.push(`${this.requireFn}.d = function(exports, name, getter) {`);
  41. buf.push(
  42. Template.indent([
  43. `if(!${this.requireFn}.o(exports, name)) {`,
  44. Template.indent([
  45. "Object.defineProperty(exports, name, { enumerable: true, get: getter });"
  46. ]),
  47. "}"
  48. ])
  49. );
  50. buf.push("};");
  51. buf.push("");
  52. buf.push("// define __esModule on exports");
  53. buf.push(`${this.requireFn}.r = function(exports) {`);
  54. buf.push(
  55. Template.indent([
  56. "if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {",
  57. Template.indent([
  58. "Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });"
  59. ]),
  60. "}",
  61. "Object.defineProperty(exports, '__esModule', { value: true });"
  62. ])
  63. );
  64. buf.push("};");
  65. buf.push("");
  66. buf.push("// create a fake namespace object");
  67. buf.push("// mode & 1: value is a module id, require it");
  68. buf.push("// mode & 2: merge all properties of value into the ns");
  69. buf.push("// mode & 4: return value when already ns object");
  70. buf.push("// mode & 8|1: behave like require");
  71. buf.push(`${this.requireFn}.t = function(value, mode) {`);
  72. buf.push(
  73. Template.indent([
  74. `if(mode & 1) value = ${this.requireFn}(value);`,
  75. `if(mode & 8) return value;`,
  76. "if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;",
  77. "var ns = Object.create(null);",
  78. `${this.requireFn}.r(ns);`,
  79. "Object.defineProperty(ns, 'default', { enumerable: true, value: value });",
  80. "if(mode & 2 && typeof value != 'string') for(var key in value) " +
  81. `${this.requireFn}.d(ns, key, function(key) { ` +
  82. "return value[key]; " +
  83. "}.bind(null, key));",
  84. "return ns;"
  85. ])
  86. );
  87. buf.push("};");
  88. buf.push("");
  89. buf.push(
  90. "// getDefaultExport function for compatibility with non-harmony modules"
  91. );
  92. buf.push(this.requireFn + ".n = function(module) {");
  93. buf.push(
  94. Template.indent([
  95. "var getter = module && module.__esModule ?",
  96. Template.indent([
  97. "function getDefault() { return module['default']; } :",
  98. "function getModuleExports() { return module; };"
  99. ]),
  100. `${this.requireFn}.d(getter, 'a', getter);`,
  101. "return getter;"
  102. ])
  103. );
  104. buf.push("};");
  105. buf.push("");
  106. buf.push("// Object.prototype.hasOwnProperty.call");
  107. buf.push(
  108. `${
  109. this.requireFn
  110. }.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };`
  111. );
  112. const publicPath = this.getPublicPath({
  113. hash: hash
  114. });
  115. buf.push("");
  116. buf.push("// __webpack_public_path__");
  117. buf.push(`${this.requireFn}.p = ${JSON.stringify(publicPath)};`);
  118. return Template.asString(buf);
  119. });
  120. }

make开始构建

  1. //开始构建模块对象
  2. this.hooks.make.callAsync(compilation, err =>{
  3. })
  1. //SingleEntryPlugin 监听make
  2. class SingleEntryPlugin {
  3. apply(compiler) {
  4. compiler.hooks.make.tapAsync(
  5. "SingleEntryPlugin",
  6. (compilation, callback) => {
  7. const { entry, name, context } = this;
  8. //创建依赖
  9. const dep = SingleEntryPlugin.createDependency(entry, name);
  10. //添加入口文件
  11. compilation.addEntry(context, dep, name, callback);
  12. }
  13. );
  14. }
  15. }
  1. //Compilation.js
  2. class Compilation extends Tapable {
  3. addEntry(context, entry, name, callback) {
  4. ...
  5. this._addModuleChain(
  6. context,
  7. entry,
  8. module => {
  9. this.entries.push(module);
  10. },
  11. (err, module) => {
  12. ...
  13. }
  14. );
  15. }
  16. _addModuleChain(context, dependency, onModule, callback) {
  17. ...
  18. //获取模块工厂
  19. const moduleFactory = this.dependencyFactories.get(Dep);
  20. this.semaphore.acquire(() => {
  21. ...
  22. //创建模块
  23. moduleFactory.create(
  24. {
  25. contextInfo: {
  26. issuer: "",
  27. compiler: this.compiler.name
  28. },
  29. context: context,
  30. dependencies: [dependency]
  31. },...)
  32. }
  33. }
  34. }
  1. class NormalModuleFactory extends Tapable {
  2. ...
  3. create(data, callback) {
  4. ...
  5. this.buildModule(module, false, null, null, err => {
  6. }
  7. }
  8. buildModule(module, optional, origin, dependencies, thisCallback) {
  9. ...
  10. //开始编译
  11. module.build(
  12. this.options,
  13. this,
  14. this.resolverFactory.get("normal", module.resolveOptions),
  15. this.inputFileSystem,...)
  16. }
  17. }
  1. //NodmalModule
  2. doBuild(options, compilation, resolver, fs, callback) {
  3. const loaderContext = this.createLoaderContext(
  4. resolver,
  5. options,
  6. compilation,
  7. fs
  8. );
  9. ...
  10. //开始运行loader
  11. runLoaders(
  12. {
  13. resource: this.resource,
  14. loaders: this.loaders,
  15. context: loaderContext,
  16. readResource: fs.readFile.bind(fs)
  17. },
  18. (err, result) => {
  19. );
  20. ) }

总结

初始化阶段

事件名 解释 代码位置
读取命令行参数 从命令行中读取用户输入的参数 require('./convert-argv')(argv)
实例化Compiler 1.用上一步得到的参数初始化Compiler实例2.Compiler负责文件监听和启动编译3.Compiler实例中包含了完整的Webpack配置,全局只有一个Compiler实例 compiler = webpack(options)
加载插件 1.依次调用插件的apply方法,让插件可以监听后续的所有事件节点,同时给插件传入Compiler实例的引用,以方便插件通过compiler调用webpack提供的API plugin.apply(compiler)
处理入口 读取配置的Entry,为每个Entry实例化一个对应的EntryPlugin,为后面该Entry的递归解析工作做准备 new EntryOptionsPlugin() new SingleEntryPlugin(context,item,name) compiler.hooks.make.tapAsync

编译阶段

事件名 解释 代码位置
run 启动一次新的编译 this.hooks.run.callAsync
compile 该事件是为了告诉插件一次新的编译将要启动,同时会给插件传入compiler对象 compiler(callback)
compilation 当webpack以开发模式运行时,每当监测到文件变化,一次新的,Compilation将被创建一个Compilation对象包含了当前的模块资源,编译生成资源,变化的文件,Compilation对象也提供了很多事件回调供插件扩展 newCompilation(params)
make 一个新的Compilation创建完毕开始编译 this.hooks.make.callAsync
addEntry 即将从Entry开始读取文件 compilation.addEntry this._addModuleChain
moduleFactory 创建模块工厂 const moduleFactory = this.dependencyFactories.get(Dep)
create 开始创建模块 factory(result,(err,module) this.hooks.resolver.tap("NormalModule")
resolveRequestArray 解析loader路径 resolveRequestArray
resolve 解析资源文件路径 resolve
userRequest 得到包括loader在内的资源文件的绝对路径用!拼起来的字符串 userRequest
ruleSet.exec 它可以根据模块路径名,匹配出模块所需的loader this.ruleSet.exec
_run 它可以根据模块路径名,匹配出模块所需的loader _run
loaders 得到所有的loader数组 results[0].concat(loaders,results[1],results[2])
getParser 获取AST解析器 this.getParser(type,setting.parser)
buildModule 开始编译模块 thislbuildModule(module) buildModule(module,optional,origin,dependencies,thisCallback)
build 开始编译 build
doBuild 开始编译 doBuild
loader 使用loader进行转换 runLoaders
iteratePitchingLoaders 开始递归执行pitchloader iteratePitchingLoaders
loadLoader 加载loader loadLoader
runSyncOrAsync 执行loader runSyncOrAsync
processResource 开始处理资源 processResource options.readResource iterateNormalLoaders
createSource 创建源码对象 this.createSource
parse 使用parser转换抽象语法树 this.parser.parse
parse 继续抽象语法树 parse(source,initialState)
acorn.parse 继续语法树 acorn.parse(code,parserOptions)
ImportDependency 遍历添加依赖 parser.state.module.addDependency
succeedModule 生成语法树后就表示一个模块编译完成 this.hooks.successdModule.call(module)
processModuleDependencies 递归编译依赖模块 this.processModuleDependencies
make后 结束make this.hooks.make.callAsync
finish 编译完成 compilation.finishi()

结束阶段

事件名 解释 代码位置
seal 封装 compilation.seal
addChunk 生成资源 addChunk(name)
createChunkAssets 创建资源 this.createChunkAssets()
getRenderManifest 获取要渲染的描述文件 getRenderManifest(options)
render 渲染源码 source = fileManifest.render()
afterCompile 编译结束 this.hooks.afterCompile
shouldemit 所有属性输出的文件已经生成好,询问插件哪些文件需要输出,哪些不需要 this.hooks.shouldEmit
emit 确定后要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容 this.emitAssets(compilation,this.hooks.emit.callAsync) const emitFiles = err this.outputFileSystem.writeFile
this.emitRecords 写入记录 this.emitRecords
done 全部完成 this.hooks.done.callAsync

webpack执行过程的更多相关文章

  1. ASP.NET Web API 过滤器创建、执行过程(二)

    ASP.NET Web API 过滤器创建.执行过程(二) 前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器 ...

  2. ASP.NET Web API 过滤器创建、执行过程(一)

    ASP.NET Web API 过滤器创建.执行过程(一) 前言 在上一篇中我们讲到控制器的执行过程系列,这个系列要搁置一段时间了,因为在控制器执行的过程中包含的信息都是要单独的用一个系列来描述的,就 ...

  3. ASP.NET Web API 控制器执行过程(一)

    ASP.NET Web API 控制器执行过程(一) 前言 前面两篇讲解了控制器的创建过程,只是从框架源码的角度去简单的了解,在控制器创建过后所执行的过程也是尤为重要的,本篇就来简单的说明一下控制器在 ...

  4. Struts2拦截器的执行过程浅析

    在学习Struts2的过程中对拦截器和动作类的执行过程一度陷入误区,特别读了一下Struts2的源码,将自己的收获分享给正在困惑的童鞋... 开始先上图: 从Struts2的图可以看出当浏览器发出请求 ...

  5. 通过源码了解ASP.NET MVC 几种Filter的执行过程

    一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...

  6. Hadoop MapReduce执行过程详解(带hadoop例子)

    https://my.oschina.net/itblog/blog/275294 摘要: 本文通过一个例子,详细介绍Hadoop 的 MapReduce过程. 分析MapReduce执行过程 Map ...

  7. 高程(4):执行环境、作用域、上下文执行过程、垃圾收集、try...catch...

    高程三 4.2.4.3 一.执行环境 1.全局执行环境是最外层的执行环境. 2.每个函数都有自己的执行环境,执行函数时,函数环境就会被推入一个当前环境栈中,执行完毕,栈将其环境弹出,把控制器返回给之前 ...

  8. saltstack命令执行过程

    saltstack命令执行过程 具体步骤如下 Salt stack的Master与Minion之间通过ZeroMq进行消息传递,使用了ZeroMq的发布-订阅模式,连接方式包括tcp,ipc salt ...

  9. Web APi之过滤器执行过程原理解析【二】(十一)

    前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授 ...

随机推荐

  1. 【vue】——使用watch 观察路由变化,重新获取内容

    更新:11-29 时隔半年,又重新使用VUE进行开发,有了新方案--beforeRouteLeave 在组件内直接使用,前提是你用了vue-router: beforeRouteLeave (to, ...

  2. Linux动态库的导出控制

    在实际工作中,许多软件模块是以动态库的方式提供的.做为模块开发人员,我们不仅要掌握如何编写和构建动态库,还要了解如何控制动态库的导出接口,这样,我们可以向模块的用户仅导出必要的接口,而另一些内部接口, ...

  3. Oracle的常用修改表及字段的语句

    单行注释:-- 多行注释:/* */ Oracle中修改表结构 增加字段     ALTER TABLE table_name ADD column_name data_type; 删除字段     ...

  4. 计算机网络课设之TCP通讯录

    这篇文章我主要是想对这学期末计算机网络课程设计所做的一个小项目也就是基于tcp协议的通讯录做一个总结梳理.项目的具体的代码实现是基于C语言,当然在此之前网上也有一些基于c++编写的tcp通讯录,原理都 ...

  5. 对drf视图的理解

    视图说明 1. 两个基类 1)APIView rest_framework.views.APIView APIView是REST framework提供的所有视图的基类,继承自Django的View父 ...

  6. mono上部署web程序初体验

    早就想体验一下mono,但一直琐事缠身.难得有时间,便在网上一通狂搜mono相关的资料. 如果想使用Apache服务器,只能使用mod_mono的方式,这里有详细的介绍.这种方式有点繁琐,需要安装一大 ...

  7. 使用webpack && react环境

    使用webpack webpack是一款模块化的打包工具,它认为所有的文件都是模块,包括js,css等等,版本为2.x推荐学习,1.x版本已废弃,不建议使用. 目前,facebook官方就是使用web ...

  8. 关于c#中”ref”和”out”关键字的一些理解

    一. 综述(本文内容大部分来自网络,经本人整理而成,仅供学习参考,不免理解错误,欢迎批评指正) 在c#中,方法的参数传递有四种类型: (1) 传值参数(by value) 传值参数无需额外的修饰符.传 ...

  9. WCF系列教程之WCF服务配置

    文本参考自:http://www.cnblogs.com/wangweimutou/p/4365260.html 简介:WCF作为分布式开发的基础框架,在定义服务以及消费服务的客户端时可以通过配置文件 ...

  10. 运行零币Zcash(ZEC)

    1.在基于 Ubuntu 或者 Debian 的系统中: $ sudo apt-get install \build-essential pkg-config libc6-dev m4 g++-mul ...