Bundler|Bundler 源码编写(模块分析)
手动编写一个很简单的类似于webpack这样的一个步骤如下:bundler(打包工具)
,在编写这个bunler的过程中,我们可以更清楚的了解到类似于webpack这样的工具,它的底层原理
- 创建文件夹以及相关文件,这几个文件作用就是为了输入一句话
say hello
,只不过里边涉及到了几个模块之间的相互调用
文章图片
image.png
文章图片
image.png
文章图片
image.png
- 我们如果想直接把src目录下的代码运行在浏览器上,肯定是不可以的,因为浏览器根本就不认识
import
这样的语法,所以我们需要一个打包工具,帮我们去做项目的打包。从而生成可以在浏览器上运行的代码。
- 我们现在创建一个
bundler.js
文件。打包工具,肯定使用NodeJS
来编写的。所以开发环境中,一定要安装了NodeJS,我这里已经是安装好了的。我们要对一个项目做打包,第一步就是要读取项目的入口文件,然后去分析入口文件的代码
文章图片
image.png
- 上边我们写的
bundler.js
这个打包工具的作用,仅仅是打印一下入口文件的内容,现在我们通过node来运行我们编写的打包工具,可以明显的看到,index.js的内容被打印了,证明我们编写的打包工具是可以用的。
文章图片
image.png
- 【Bundler|Bundler 源码编写(模块分析)】但是我们在终端打印出的内容颜色不太明显,我们怎么解决这个问题呢?我们需要安装一个高亮显示代码的工具
npm install cli-highlight -g
文章图片
image.png
- 安装好以后,我们再运行 bundler.js,通过后边跟管道符,管道符后边跟 highlight,然后打印出的内容,就会有一个颜色标识,更加明显
文章图片
image.png - 现在我们已经拿到了入口文件的代码内容了,接下来第二步,我们要拿到这个模块它的依赖。在我们的例子中,index.js的依赖是
message.js
这个文件,我们把message.js
从代码里提取出来,怎么提取呢? 总不能用字符串截取吧,如果它引入了很多模块,那么这种方式肯定是不行的,太麻烦。我们需要一个更加方便准确的方式,引入一个第三方模块,这个模块跟Babel
相关,Babel
可以帮我们去分析我们语法,这个模块叫做@babel/parser
文章图片
image.png
npm install @babel/parser --save
- 安装好以后,我们就可以使用了
文章图片
image.png
文章图片
image.png
文章图片
image.png - 我们写好以后,看下打印出了什么内容,运行
node bundler.js | highlight
,可以看到,打印出了一个JS对象,其实这个对象就是抽象语法树(AST)
,这个对象可以很好的表述当前的这段代码,
文章图片
image.png
- 我们打印一下抽象语法树里的body,我们可以看到,这两个东西就是program里的内容,对应的节点。
文章图片
image.png - 第一个节点是一个引入的声明
文章图片
image.png
文章图片
image.png - 第二个节点是一个表达式语句
文章图片
image.png
文章图片
image.png - 所以,我们调用
@babel/parser
的parse方法,就可以帮我们分析出抽象语法树,通过抽象语法树,就可以找到一些声明的语句,而声明的语句里,放的就是入口的文件里对应的一些依赖关系,假设我们的源码里,再加一个引入。然后再重新执行node bundler.js | highlight
文章图片
image.png
文章图片
image.png
- 我们可以清楚的看到,第一个和第二个节点,都是引入声明, 第三个还是表达式语句,这个抽象语法树,可很好的把我们的JS代码转化成了一个JS对象,我们现在要做的事,拿到这个代码里,所有的依赖关系,我们可以去遍历
body
,找到body里 type等于ImportDeclaration
这样的一些内容,但是自己写遍历的话,还是有点麻烦。Babel
还提供给我们了一个模块,可以帮我们快速的找到import的节点,所以我们要安装这样的一个模块@babel/traverse
npm install @babel/traverse --save
- 安装好后,我们来使用这个模块
文章图片
image.png - 运行命令,分析出我们有两个
ImportDeclaration
,对应我们源码里两个import语句
文章图片
image.png - 我们可以看到,通过node的source下边的value可以取到我们想要的值
文章图片
image.png - 然后我们把取到的值,存入一个数组,并且打印
文章图片
image.png - 然后再运行命令 ,我们看到了入口文件的依赖文件都有哪些了
文章图片
image.png - 这个时候我们发现,我们获取到的依赖文件,都是相对路径,而且是相对于入口文件index.js的路径,但是真正在做代码打包的时候,我们需要这些依赖文件,不能是相对路径,必须是一个绝对路径,或者说,即便是相对路径,也是相对于bundler这个根路径才可以,这样打包才不会有问题。所以我们的dependencies数组里存的不可以是相对于入口文件的相对路径,而要是绝对路径或者相对于bundler这个根路径的相对路径,要怎么实现呢?
path
,该模块提供了一些用于处理文件路径的小工具,dirname
方法返回路径中代表文件夹的部分文章图片
image.png
文章图片
image.png
22.然后我们再调用
join
方法拼接一下路径,然后再打印,就打印出绝对路径了
文章图片
image.png
文章图片
image.png
- 如果我们的dependencies 只存一个
./src/message.js
这个的路径的花,后边打包还是会比较麻烦,如果我们把相对路径和绝对路径都存进去。那么我们怎么操作呢?
文章图片
image.png
文章图片
image.png
-
{ './message.js': './src/message.js' }
这里的意思是,我知道你原始引入的是message.js
,但是实际上真正对应的项目中的文件是根路径下的src目录下的message.js - 如果我们有多个依赖呢? 再次运行命令,可以清楚的看到都有哪些依赖
文章图片
image.png
文章图片
image.png
26.然后我们就可以把这些已知的都return出去
文章图片
image.png - 当然,我们单纯把入口文件和依赖return出去是没于意义的,因为我们的入口文件 是
ESModule
形式的,浏览器是无法运行的,我们还需要借助babel
对语法进行转化,转换成浏览器可以运行的代码。此时我们需要安装一个模块@babel/core
,这是一个babel
的核心模块
文章图片
image.png - 安装成功后,我们需要 引入,并且调用
transformFromAst
方法,这个方法可以把AST抽象语法树,转化成浏览器可以运行的代码
文章图片
image.png - 然后安装
@babel/preset-env
模块,把这个模块配置到presets
里
npm install @babel/core --save
文章图片
image.png
- 然后再运行命令,可以看到,代码已经不是入口文件index.js里的代码了,而是编译过之后可以在浏览器上运行的代码
文章图片
image.png
文章图片
image.png
- 写到这里,我们对入口文件的代码分析就结束了,我们可以打印一下返回出的结果
文章图片
image.png
文章图片
image.png - 然后我们可以看到,我们对入口文件进行分析后,可以清楚的知道以下几点信息:
1. 入口文件对应的是`./src/index.js`;
2. 入口文件的依赖是message.js文件,这个文件真正的路径是'./src/message.js'
3. 它被翻译过后,要在浏览器上运行的代码是code部分
通过以上代码,我们已经可以实现对一个JS模块的代码分析了
推荐阅读
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- Android事件传递源码分析
- 编写字典程序
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- 用npm发布一个包的教程并编写一个vue的插件发布
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)