webpack2的那些事儿 ------ 生成的文件是怎么运行的

谢谢大家看我扯技术,最近在对webpack2进行的配置进行梳理和学习,webpack是在去年使用vue开始接触的,我的感受webpack 融入到编程过程当中,提供了模块化,将各类类型的文件都当作模块,经过不一样的 loader 进行处理和代码组织,是一个比较新颖的编程体验,应该说webpack的编程适用场景比较普遍,可以比较方便的引入第三方的各类 npm 模块进行使用, 方便快速开发工做。
打算写几篇文章(若是能坚持的话= =)来总结下 webpack,文章不是教你怎么使用webpack,而是让你更好的了解你在使用的webpack是怎么去运行的 ,想来想去,第一篇就先介绍下webpack生成的文件,是怎么去执行的。html

webpack 的生成信息

首先咱们要先经过 webpack 去生成文件(好一句废话),文章全部的代码都会在文章最后面给出连接,下面是本文章使用的代码的目录:
图片描述vue

咱们如今只要关注js目录,里面有两个入口 app.jsbar.js,而后会引用 es5,es6中的各类测试模块,具体你们能够看代码。而后代码一跑!只见命令行蹭蹭蹭跑出来了好多信息,像下面同样:
图片描述webpack

首先咱们来看下生成的信息:git

  • Asset : 这个一看就明白是生成的文件相对于配置中output.path的路径,能够看到图中生成的文件都是在 output.path底下的;而后咱们仔细看下文件名,好比第一个0.fb6d7f4.js,是由[name/chunkname].[hash/chunkhash].js组成的,这个能够在output.filename 中配置,关于hashchunkhash的区别,这个后面会专门经过一篇文章进行简介。es6

  • Size : 这个就没啥好说的,就是生成文件的大小github

  • Chunks : 咱们会看到有些 Chunks是两个数字,有些是一个,其实还可能出现更多,通过个人一堆实验= =,发现Chunks中的第一个数字,就是这个文件的 ChunkId,然后面的是当前这个文件依赖的文件的ChunkId,从图中咱们能够看到,第一个文件的ChunkId0,它依赖的是ChunkId3manifest.a890c12.jsweb

  • Chunk Names : 这个就是这个生成文件的chunkName,能够用于文件命名,能够看到若是没有在entry中指定,那么chunkName会等于chunkIdchrome

程序加载流程

了解了生成的信息,接下来咱们把项目跑起来(能够用 anywhere 跑项目),经过chrome developer tool能够看到请求状况图片描述npm

能够看到请求了页面html以后,按顺序分别加载了 manifest,index,0,2文件,这里咱们先来分析下文件的分割和加载流程。编程

分割

能够看到页面的 js 被分割成为了4个文件,一般来讲,一个项目定义了一个 entry point,
webpack会以这个entry point做为入口,进行代码回溯,若是存在System.import或者是require.ensure的异步模块调用,webpack会对使用的模块进行单独打包,好比文件中的02这两个 js,若是没有异步模块调用,那么会将全部的代码生成在一个文件中,webpack 为了使得打包的代码进行优化,可使用CommonsChunkPlugin插件对代码进行处理,将库文件单独打包,经过规则生成对应的 chunk 文件,其中的manifest为 默认的 chunk,其中包含了打包文件的runtime信息,还有webpackJsonp模块加载的封装库,全部的生成模块都是采用webpackJsonp进行封装的。

manifest

从上面的图中能够看到,浏览器按顺序分别加载了 manifest,index,0,2文件,其中manifest至关于webpackruntime工具,用于作模块加载,其余文件是逻辑文件; manifest中封装了webpackJsonpCallback方法和__webpack_require__方法,下面咱们来进行分析:

  • webpackJsonpCallback(chunkIds, moreModules, executeModule):webpackJsonpCallback是chunk封装的包装方法,webpack在生成每个chunk的时候都是经过这个方法进行包装的,咱们在上面看到的 chunksId,会做为第一个参数,被包含进这个chunkmodule会被以数组的形式传入第二个参数moreModules中,若是这个chunk中包含能够执行的modules,须要将 moduleId传入第三个参数 executeModule中,下面是 这个方法的代码片断:
    图片描述这个方法主要作了下面几件事:

    • 加载chunk

      咱们能够看到这个方法用第一个循环分别将chunkIds处理进入installedChunks对象中,installedChunks对象用于记录chunk的加载状况,分别用0表示当前的chunkId已经加载完成,用一个长度为3的数组表示当前的chunk正在加载中,数据中其实存储着加载过程当中的resolve方法、reject方法和pormise对象,这种只在经过require.ensure或者是System.import才会出现。所以咱们能够看到,第一个for循环中判断若是chunkId在 installedChunks 中存在且不为0,则判断是异步加载的模块已经加载成功,将chunkresolve方法传入resolves数组,而后后面运行,而后将chunk对应的状态设置为0。若是判断以后不存在,这认为这是一个同步加载的chunk,直接设置为0,表示chunk已经加载完毕。

    • 加载 module

      加载 module 的逻辑比较简单,判断纯不存在这个module以后,将 其写入modules参数之中

    • 运行须要执行的module

      若是executeModule存在,则对其中对应moduleId的模块进行运行

  • __webpack_require__: 这个对象包含了多个方法,主要用于modulechunk的加载,处理和运行,下面咱们一个一个分析:

    • __webpack_require__(moduleId) :代码以下
      图片描述 这个方法接收一个moduleId,构建一个 module 对象存入installedModules中,而且初始化这个 module, 最后返回module.export

    • __webpack_require__.e(chunkId) : 这个方法用于经过异步的方式加载 chunk 文件,代码以下:
      图片描述这个方法整体来讲就是加载一个 script 文件,生成一个 promise对象,当 script 加载完成后运行,又会执行前面的webpackJsonpCallback注册chunk,而后promise.resolve。这里面须要注意的是红框里面的东西,这个涉及到一个优化点,若是没有在使用CommonsChunkPlugin单独打包manifest,那么通常来讲他会和你指定的其余库经过CommonsChunkPlugin打包在一块儿,那么你会发现即便你只是修改了库以外的逻辑,库文件生成的文件的hash或者是chunkhash也是会变的,缘由就在于manifest中红框部分是动态生成的,致使文件的 hash 产生变化,不利于缓存,所以建议单独打包manifest

    • __webpack_require__.oe:定义一个统一的错误处理函数

    • __webpack_require__.p:这个是和webpackoutput.publicPath对应的值

    • __webpack_require__.o: Object.prototype.hasOwnProperty的封装
      前面几个方法在 ES5的情景下面已经足够运行这个模块系统,咱们都知道webpack2加入了对ES6 MODULE的支持,下面几个__webpack_require__是为ES6使用的:

    • __webpack_require__.d:代码以下:
      图片描述这个是用于ES6中命名的export好比 图片描述 webpack 遇到这种export,会对其用__webpack_require__.d进行包装,变成: 图片描述

    • __webpack_require__.i:用于返回一个正确的上下文的函数回去,针对的是export直接为一个可运行方法的时候

以上就是webpack manifest中的大部分重要的函数,其实主要就是经过webpackJsonpCallback来注册载入对应的chunk文件,经过__webpack_require__来处理模块的关系。

总结

整个webpack 的在运行时都是经过 manifest去作控制处理的, webpackJsonpCallback对应的是对加载的chunk文件的处理,__webpack_require__是对加载模块的处理,了解这些可使咱们更好的去优化咱们的代码,帮助咱们去调试代码,帮助咱们在复杂状况下去解决问题提供一些其余的思路。

最后附上代码:先介绍下,webpack-base是我在使用webpack的过程当中本身总结的一套脚手架,文档尚未完善,若是须要文档能够在issue里面提,本次的项目在分支上面开发,代码点击这里

相关文章
相关标签/搜索