-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
webpack源码学习系列之一:如何实现一个简单的webpack #99
Labels
Comments
👍 |
mark |
youngwind
changed the title
webpack源码学习:如何实现一个简单的webpack
webpack源码学习系列之一:如何实现一个简单的webpack
Feb 10, 2017
66666 |
我想知道怎么执行 |
请教一下:你怎么能找到webpack的第一个commit时的镜像的? |
|
想请问下webstorm里怎么调试 |
webstorm 有 debug 功能,你可以参考一下这里:http://www.cnblogs.com/jinguangguo/p/4809886.html @tanxuewei |
@youngwind 用vscode调试了,谢谢 |
好多人.明明的官方的文档,死活就是不看,非得百度 |
node bin/webpack.js ./examples/simple/example.js; vscode里面,打开launch.json,配置如下:
|
从头撸 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前言
在上一篇 #98 中,我们通过实现requireJS,对模块化有了一些认识。今天我们更进一步,看看如何实现一个简单的webpack,实现的源码参考这里。
目标
现在的webpack是一个庞然大物,我们不可能实现其所有功能。
那么,应该将目光聚焦在哪儿呢?
从webpack的第一个commit可以看出,其当初最主要的目的是在浏览器端复用符合CommonJS规范的代码模块。这个目标不是很难,我们努力一把还是可以实现的。
注意:在此我们不考虑插件、loaders、多文件打包等等复杂的问题,仅仅考虑最基本的问题:如何将多个符合CommonJS规范的模块打包成一个JS文件,以供浏览器执行。
bundle.js
显然,浏览器没法直接执行CommonJS规范的模块,怎么办呢?
答案:将其转换成一个自执行表达式
注意:此处涉及到webpack构建出来的
bundle.js
的内部结构问题,如果不了解bundle.js具体是如何执行的,请务必搞清楚再往下阅读。可以参考 #64 或者这里例子
我们实际要处理的例子是这个:example依赖于a、b和c,而且c位于node_modules文件夹中,我们要将所有模块构建成一个JS文件,就是这里的output.js
思路
仔细观察output.js,我们能够发现:
example依赖于a、b和c
。require('c')
,这里肯定是存在某种自动查找的功能。output.js
中,每个模块的唯一标识是模块的ID,所以在拼接output.js
的时候,需要将每个模块的名字替换成模块的ID。也就是说,ok,下面我们来逐一看看这些问题。
分析模块依赖关系
CommonJS不同于AMD,是不会在一开始声明所有依赖的。CommonJS最显著的特征就是用到的时候再
require
,所以我们得在整个文件的范围内查找到底有多少个require
。怎么办呢?
最先蹦入脑海的思路是正则。然而,用正则来匹配
require
,有以下两个缺点:require
是写在注释中,也会匹配到。require
的参数是表达式的情况,如require('a'+'b')
,正则很难处理。因此,正则行不通。
一种正确的思路是:使用JS代码解析工具(如esprima或者acorn),将JS代码转换成抽象语法树(AST),再对AST进行遍历。这部分的核心代码是parse.js。
在处理好了
require
的匹配之后,还有一个问题需要解决。那就是匹配到require
之后需要干什么呢?举个例子:
这里有三个
require
,按照CommonJS的规范,在检测到第一个require
的时候,根据require即执行
的原则,程序应该立马去读取解析模块a
。如果模块a
中又require
了其他模块,那么继续解析。也就是说,总体上遵循深度优先遍历算法。这部分的控制逻辑写在buildDeps.js中。找到模块
在完成依赖分析的同时,我们需要解决另外一个问题,那就是如何找到模块?也就是模块的寻址问题。
举个例子:
在模块
example.js
中,调用模块a、b、c
的方式都是一样的。但是,实际上他们所在的绝对路径层级并不一致:
a和b
跟example
同级,而c
位于与example
同级的node_modules
中。所以,程序需要有一个查找模块的算法,这部分的逻辑在resolve.js中。目前实现的查找逻辑是:
当然,此处实现的算法还比较简陋,之后有时间可以再考虑实现逐层往上的查找,就像nodejs默认的模块查找算法那样。
拼接output.js
这是最后一步了。
在解决了
模块依赖
和模块查找
的问题之后,我们将会得到一个依赖关系对象depTree
,此对象完整地描述了以下信息:都有哪些模块,各个模块的内容是什么,他们之间的依赖关系又是如何等等。具体的结构如下:根据这个
depTree
对象,我们便能完成这最后的一步:**output.js文件的拼接。**其控制逻辑无非是一层循环,写在writeChunk.js中。但是这里有一个需要注意的地方,那就是本文思路章节提到的第4点:要把模块名转换成模块ID,这是writeSource.js所要完成的功能。
至此,我们就实现了一个非常简单的webpack了。
遗留问题
require('a' + 'b')
这种情况。参考资料
========EOF===========
The text was updated successfully, but these errors were encountered: