webpack在web前端开发领域可谓占据了很重要的位置,我们的vue和react项目基本都会通过webpack来进行打包构建,最后生成可被浏览器执行的代码包。
webpack是一个流行的 静态模块打包器。它通过 “打包” 这种方式很好的简化了web开发流程。用过grunt和gulp的都知道他们是基于任务流的,需要靠你自己去设定好一套处理流程。webpack另辟蹊径的使用了打包这种方式,极大的简化了操作。
所谓打包对于web前端开发来说就是把整个项目的所有内容进行处理后生成少量的几个可以被浏览器使用的文件。
那它如何做到这一点的呢?很重要的一点就是 “依赖图”
当它处理你的项目的时候,会将你项目中引入的任何文件都当做一个模块来处理,一个模块引入了另一个模块,他们之间就存在依赖关系,层层递进这种依赖关系之后,就可以得出一个“依赖图”(依赖图的样子就和webpack官方文档的首页那个图左边部分是类似的)。如下图:
对于构建依赖图的过程,我们可以想象webpack就好像是一个具备传染性的病毒,一开始感染了一个人,然后再感染这个人所接触到的人,然后那些人又会不断感染下去,这样就构成了“感染图”,也就是我们webpack中的依赖图。
有了依赖图,就相当于对你整个项目的构成了如指掌了,那就可以一个不漏的把项目的模块凑到一块儿,形成一个或者多个打包文件。这就是webpack做的事儿。
一句话来说就是:对webpack来说什么都是模块
不仅仅是js、json文件。一张图片、一个字体文件、都可以看做模块,webpack不管它是什么,只要引入了就当做一个模块。那图片和js这种的根本就没法放到一起相提并论的东西,webpack怎么能打包到一起? 这就是之后要提到的Loader的概念来解决的了。
在Nodejs创造出来的时候就自带有模块化编程的特性,不过对于浏览器来说来得比较晚,因此也出现了很多浏览器端的模块化编程的解决方案。webpack从这些模块化系统中吸取经验,并将模块化这一概念应用到了你的项目中的任何一个文件上。所以对其他模块化的系统来说特定的文件才能作为模块,对webpack来说,任何文件都可以看做模块。
对于webpack来说,以下语法都算是引入了一个模块:
依赖图其实就是根据依赖关系形成的一个逻辑关系图。 (参见前面那张图) 你也可以直接参见我们列举的病毒感染的例子
要弄清楚这一点,首先得清楚什么叫做依赖。我们说了模块的概念,依赖 其实就是 当一个模块引入另一个模块的时候,我们说这个模块依赖了另一个模块。因为你想啊,我引入另一个模块那就可能是要用它里面的东西,那如果没有它里面的东西,当前的这个模块就不能用,所以当前模块对要引入的模块之间就是存在依赖的。
一个文件可以依赖多个文件,每一个被依赖的文件也可能有自己的零个或者多个依赖项。把这些依赖画出来,就得到一个表示依赖关系的依赖图。
其实从前面那张图里面我们也看到了, 如果要构建一个依赖图出来,至少得有个起始位置,这个起始位置,在webpack中叫做 入口点 ( 也就是病毒感染那个例子中第一个被感染的人) 。从入口点出发,webpack就像一个病毒一样的,不断递归式的去搜寻所有这个项目涉及到的依赖项。所以,指定一个入口点,webpack就可以自己开始工作了。
既然入口点给了,webpack也能自己寻找项目的依赖并进行打包了。但是最终生成的文件放在哪里,这个你就得先告诉webpack了,如果不先告诉它,它会使用默认的输出位置(在webpack V4以上是 ./dist/main.js),生成最终的打包文件。
Loader可以说是webpack中非常重要的一个概念了,事实上,webpack只能识别js和json类型的文件,对于css、vue、less、sass、图片、字体这样一些文件是根本不能处理的。我去,那都不能处理,怎么可能打包。
所以webpack有了loader这样的东西,loader是干嘛的呢?不是说css、vue等这一类的文件不能被处理吗,那就找个能处理的工具来处理它。我webpack就只管找依赖和处理我能处理的部分就得了,不能处理的就使用loader来处理,loader处理之后给我结果。就比如你是公司的领导者,你可以发布战略,可你不会写程序,那让程序员去写呗;你不会运营,那找运营人员去干呗;你不懂法务,那请一个法务来帮你呗。专门的人干专门的事儿,最后总结汇报给webpack。这样一来,webpack就真的是可以把所以文件都当做模块,虽然它自己不能处理,但是可以找loader处理啊,loader处理之后给它结果不就得了。
处理css用css-loader和style-loader,处理图片用file-loader或者url-loader,处理less用less-loader,处理es6语法用babel-loader。你看,每个原本webpack不能处理的内容,都可以找到相应的处理工具。(具体每个不同的loader的作用是什么,以及怎么使用,建议在 npmjs.com 上去搜)
以上就是loader,以及它和webpack之间的关系的说明。
之前的过程中webpack寻找构建依赖图,然后不能处理的内容交给loader搞定,最后生成打包文件,看起来好像没啥可说的了。不过其实上loader能做的任务比较专一也比较有限,有的方面的事情就不是loader可以做的,比如打包之前清除旧的文件、在打包过程中展示进度 等等。这些谁来做呢,webpack说我给你提供一个plugin的机制,你要扩展我的功能的话,就加上plugin就可以了。
So, plugin实际上就可以达到扩展webpack功能特性的操作,你可以在plugin中对webpack的整个构建流程做处理。
实际上拥有了Plugin机制后的webpack,也就具备了可以像gulp和grunt这样的任务处理器一样的能力,因此我们虽然webpack主要效用是打包用的,但是也可以承接一些任务处理的工作内容,这样你也不用再去使用其他工具了。
runtime和manifest这两个东西是至关重要的概念,所以不得不说一下。我们的一个webpack打包的项目一般会涉及到三种类型的代码(这里可以记下来):
runtime和manifest是紧密配合的兄弟,runtime就是我们的运行时,它相当于一个引擎。我们的项目会有各种自己写的代码,也会有很多第三方库代码,这些代码最终都是一个个的模块,在编译之后这些模块是如何有效的调用起来,让应用运行起来的呢?就是runtime这个引擎在起作用,它是webpack在构建的时候生成的一系列代码,能够把所有构建过程中涉及的模块有效的结合起来并在浏览器中使用。
但是只有runtime还是不够的,如果说runtime是一个引擎,那么它还需要manifest这个“燃油”才能真正启动起来。这个比喻可能还不是很恰当。不过我们可以把manifest想象成一个记录表,它详细记录了你的模块信息以及模块与模块之间的调用关系。你想啊,runtime是可以进行模块调用,但是调用谁呢?这个要调用的东西在什么地方呢?所以这样的一些信息在代码打包的过程中就应该被记录下来,当做一个标准文档,runtime在执行的时候需要的那些信息就从manifest中获得,从而才能更好的结合所有的模块使得整个应用完整正确的运行起来。
有了这几个基础概念,我们就可以开始准备进入具体的配置环节了。