【webpack】webpack打包规律笔记

前言

  • 由于以前学习时新知识太多,导致结果过于注意细节实现,从而忽略整体究竟怎么工作。这次重刷webpack理清下思路。
  • 而且以前特别特别讨厌学webpack,最近学新知识发现有涉及webpack,所以必须得理一下。

webpack打包规律

  • 最简单的流程上说,webpack打包就是在页面引入了自己构建好的js文件。而这个js文件里有个自执行函数,里面的参数,就是webpack从各个文件中拿到你编写的项目根路径作为键,文件内容作为值。

  • 为了能让编写的文件工作,这个Js里面有一套方法,同时让编写文件里的require通过ast换成webpack自己的require。通过执行这套方法,得到最终的返回对象。

  • webpack的r函数通过Symbol.toStringTag给对象打上module属性,同时也附上__esModule的属性。

  • n函数就是把默认导出拿出来的函数。es6模块会被r函数打上esModule属性,默认导出会在default上。而commonjs拿到的默认导出会赋值给函数的a属性,最后返回这个函数。

  • t函数就是通过位运算当条件判断用,传来的value可能是模块id,也可能是模块对象,1248分别就是2进制4个位。与1为true表示传来的最后一位就是二进制1,代表传来的值是个模块id,用webpackrequire导入value重新赋给它。与1和与8是前2个执行的,如果与8是true,那么直接返回value(如果这个value是模块,前面与1的时候已经赋值给它了。)。与4并且value是对象,且value是esmodule就返回value(也就是esModule是先传入模块id,然后通过与1获得导出对象,然后通过与4返回真正的导出内容)。与2是把所有属性放到下面说的空对象上。如果没走与4(与4还有个判断es属性,所以可能就算与4是true也进不来分支)或者与8,就比较复杂,需要强行包装成es6模块,一般是commonjs的导出比如:

module.exports={1:111}
  • 这种形式没有default,就先创个空对象,然后r函数加上es模块属性,把value放这个对象的default上。这时如果与2是true,且value不是字符串,就把value上所有属性拷贝到这个新对象上。到此就把这个模块转成了es模块。最后在把这玩意返回就行。
  • 所以像上面那个模块,最后就变成这样:
{
__esModule:true,
default:{1:111}
1:111
}
  • 如果没有与2,上面那个处理后就只有default,没有下面那行属性。
  • 再总结下4个数字用途,与1表示value是id,处理后拿到值。与4表示是es6模块,直接返回。与8表示直接返回,啥都不用干。与2表示拷贝所有属性到对象上。
  • comonjs导入导出就直接webpackrequire导入,导出部分属性直接挂上就行了。
  • es模块导出,commonjs模块导入。导入部分不变,导出部分会被改写,先走r函数,表示导出时es模块,然后走d函数,把导出对象加上属性(这个属性就是普通导出的属性),再给其赋值default属性,最后webpack_exports对象就挂上了默认导出和普通导出。
  • es模块导入导出。导出部分和上一条一样,相同方式改写。导入部分会先走r函数(其实意思是这个模块如果要导出,也是es模块)。然后声明个变量,用webpackrequire获得导出对象的普通导出和默认导出。
  • es模块导入commonjs模块导出。导出部分不进行转换。导入部分和上一条不一样的地方在这个变量通过webpackrequire拿到的还不是默认导出,声明了另一个变量通过n函数获得获取comonjs的默认导出,最终默认导出在这个变量的a属性上。其他跟上一条一样
  • 异步加载模块:
    这个有点复杂,先是给window对象加个webpackJsonp属性,这个属性等于一个数组。然后把这个数组的push方法的指针绑在数组上。并把数组的push方法换成webpackJsonpCallback方法。也就是异步加载的模块调用push方法就会调用webpackJsonpCallback。
    异步模块会被改写成调用这个push方法,参数是个数组,数组里面一个是chunk名数组,一个是其内容对象(键名是chunk路径)。
  • 一开始主文件里异步加载部分会写import,这个import会替换成e函数。而e函数里面会拿到webpack一开始定义的缓存对象,看有没有加载过,如果没有加载过,并且不在加载中,就new个promise,将resolve和reject变成数组,把promise作为第三个元素放到缓存对象上。最后promise存进一个promises的数组。然后就创建script标签,src就变成chunkid。 然后会做异常处理,超时处理,最后把script标签放到head里。这个e函数最终返回的是Promise.all(promises)结果。
  • 当这个script插入head,就会调异步的模块了,执行那个push方法。
  • 由于push方法被改成callback方法,就执行callback里面内容,这个函数先把传来的chunkid和内容拿到,然后去缓存对象里找这个id,这个缓存对象里是有这个id的。因为前面被e方法把值变成了promise的resolve和reject。然后就把resolve方法全拿出来,作为数组存起来,把总模块里加上这个chunk,缓存里把这个chunk状态变为0,就是已装载,再依次执行resolve数组。
  • resolve都执行完,promiseall就都会成功,e方法的promiseall就完成了。然后就是走promiseall后面的then方法(这个then还不是用户调的then),这时,就会走t方法,进行包装,包装完毕后拿到模块导出返回值对象,最后走用户调的then方法。所以说,这个t函数就是处理各种模块的异步导入的,最后获取的返回值就是用户then拿到的对象。
  • 其实前面的jsonp操作就是为了在特定时机让模块装载调页面上函数执行resolve以确保装载完成。真正执行还是t方法对其包装需要拿到其导出。

总结

  • 这样写一遍基本把流程理清了,打包里最特别的还是t函数位运算那,据说是因为这代码要插到浏览器,这样写就可以只花1个字节就能完成判断逻辑。
  • 其次promise+jsonp的组合也很秀。先页面上写好promise,异步模块里调用页面上callback方法,callback方法可以拿到promise的resolve。形成一个完整闭环。
发布了178 篇原创文章 · 获赞 11 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yehuozhili/article/details/105290280