手写一个webpack,看看AST怎么用 (2)

这个配置文件里面其实只要指定了入口文件entry和编译后的输出文件目录output就可以正常工作了,这里这个配置的意思是让webpack从./src/index.js开始编译,编译后的文件输出到dist/main.js这个文件里面。

这个配置文件上还有两个配置mode和devtool只是我用来方便调试编译后的代码的,mode指定用哪种模式编译,默认是production,会对代码进行压缩和混淆,不好读,所以我设置为development;而devtool是用来控制生成哪种粒度的source map,简单来说,想要更好调试,就要更好的,更清晰的source map,但是编译速度变慢;反之,想要编译速度快,就要选择粒度更粗,更不好读的source map,webpack提供了很多可供选择的source map,具体的可以看他的文档。

然后就可以在dist下面建个index.html来引用编译后的代码了:

// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <script src="http://www.likecs.com/main.js"></script> </body> </html>

运行下yarn build就会编译我们的代码,然后打开index.html就可以看到效果了。

image-20210203154111168

深入原理

前面讲的这个例子很简单,一般也满足不了我们实际工程中的需求,但是对于我们理解原理却是一个很好的突破口,毕竟webpack这么庞大的一个体系,我们也不能一口吃个胖子,得一点一点来。

webpack把代码编译成了啥?

为了弄懂他的原理,我们可以直接从编译后的代码入手,先看看他长啥样子,有的朋友可能一提到去看源码,心理就没底,其实我以前也是这样的。但是完全没有必要惧怕,他编译后的代码浏览器能够执行,那肯定就是普通的JS代码,不会藏着这么黑科技。

下面是编译完的代码截图:

image-20210203155553091

虽然我们只有三个简单的JS文件,但是加上webpack自己的逻辑,编译后的文件还是有一百多行代码,所以即使我把具体逻辑折叠起来了,这个截图还是有点长,为了能够看清楚他的结构,我将它分成了4个部分,标记在了截图上,下面我们分别来看看这几个部分吧。

第一部分其实就是一个对象__webpack_modules__,这个对象里面有三个属性,属性名字是我们三个模块的文件路径,属性的值是一个函数,我们随便展开一个./src/helloWorld.js看下:

image-20210203161613636

我们发现这个代码内容跟我们自己写的helloWorld.js非常像:

image-20210203161902647

他只是在我们的代码前先调用了__webpack_require__.r和__webpack_require__.d,这两个辅助函数我们在后面会看到。

然后对我们的代码进行了一点修改,将我们的import关键字改成了__webpack_require__函数,并用一个变量_hello__WEBPACK_IMPORTED_MODULE_0__来接收了import进来的内容,后面引用的地方也改成了这个,其他跟这个无关的代码,比如const world = 'world';还是保持原样的。

这个__webpack_modules__对象存了所有的模块代码,其实对于模块代码的保存,在不同版本的webpack里面实现的方式并不一样,我这个版本是5.4.0,在4.x的版本里面好像是作为数组存下来,然后在最外层的立即执行函数里面以参数的形式传进来的。但是不管是哪种方式,都只是转换然后保存一下模块代码而已。

第二块代码的核心是__webpack_require__,这个代码展开,瞬间给了我一种熟悉感:

image-20210203162542359

来看一下这个流程吧:

先定义一个变量__webpack_module_cache__作为加载了的模块的缓存

__webpack_require__其实就是用来加载模块的

加载模块时,先检查缓存中有没有,如果有,就直接返回缓存

如果缓存没有,就从__webpack_modules__将对应的模块取出来执行

__webpack_modules__就是上面第一块代码里的那个对象,取出的模块其实就是我们自己写的代码,取出执行的也是我们每个模块的代码

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxzpz.html