什么是webpack?
一、前言
webpack真的是做前端的程序员们一项必备的技能了。如果你之前接触过webpack但又不太明白它到底是做什么的,或者你了解的比较浅薄,没有深入了解过,那么你应该仔细阅读这篇文章。读完这篇文章之后,希望你已经对webpack有一个全方面大致的了解。
二、为什么选择webpack?
那我们为什么选择webpack呢?首先我这里有几个问题,我希望你读这篇文章的时候带着问题去阅读,读完这篇文章之后,我希望你不仅能学习到了webpack,也能学习到了这种学习方式。
- 为什么会出现webpack?同类或是类似的工具有什么不好?
- webpack给我们带来了哪些新的思路?解决了哪些问题?
- 发明人对我们有什么建议,可以更好的去使用webpack?
三、为什么会出现webpack?
现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法,例如:
- 模块化,让我们可以把复杂的程序细化为小的文件;
- 类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能转换为JavaScript文件使浏览器可以识别;
- Scss,less等CSS预处理器;
- …
这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为webpack类的工具的出现提供了需求。
四、什么是webpack?
让我们翻阅官方文档,官方的定义是,webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
五、webpack和Grunt以及Gulp相比有什么区别?
其实webpack和另外两个并没有太多的可比性,gulp 和 webpack 并不能直接比较,前者是一个 task
runner,而后者是一个 module bundler,它们两者之间都有一些相互不可替代的功能。webpack 的插件是面向配置的,Gulp/Grunt是面向过程的。
Gulp/Grunt是一种能够优化前端的开发流程的工具,而webpack是一种模块化打包的解决方案,不过webpack的优点使得webpack在很多场景下可以替代Gulp/Grunt类的工具,也就是很多时候如果不是有特殊的开发需求,那么webpack就已经够用了。
Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤。Gulp 管道的概念,即从源头那里得到源数据(js/css/html 源码、图片、字体等等),然后数据通过一个又一个组合起来的管道,最后输出成为构建的结果。工具最后可以自动替你完成这些任务,如图。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
六、webpack之source-map
简单说,Source map就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。
我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩,去掉多余的空格,且babel编译化后,最终会用于线上环境,那么这样处理后的代码和源代码会有很大的差别,当有bug的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发不好调式,因此sourceMap出现了,它就是为了解决不好调式代码问题的。
有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码。这无疑给开发者带来了很大方便。
在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:
devtool:value | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度; |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便; |
eval-source-map | 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项; |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点; |
以下是实例代码:
module.exports = {
devtool: 'eval-source-map'
}
七、webpack之loaders
通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css;比如babel-loader,把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件;比如React的开发,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。
比如耳熟能详的css-loader和style-loader:
//使用
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
}
]
}
]
}
};
又或者说我们的babel-loader,将JSX语法或者ES6/7语法转换为浏览器可执行的JS:
module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
]
}
};
八、webpack之plugins
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less…),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。
Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能,以下是一些常用的插件:
(这里有个缺点就是官方的文档并不是写的很好,很多插件的作用以及原理都没有写出来,对于开发还是有一定的不便利的。)
插件名 | 配置效果 |
---|---|
HtmlWebpackPlugin | 这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。 |
Hot Module Replacement | Hot Module Replacement(HMR)也是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。 |
OccurenceOrderPlugin | 为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID。 |
UglifyJsPlugin | 压缩JS代码。 |
ExtractTextPlugin | 分离CSS和JS文件。 |
… | … |
以下示例配置代码:
module.exports = {
...
devServer: {
hot: true
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/public/index.html"// 指定模板路径,并传入相关的参数
}),
new webpack.HotModuleReplacementPlugin(), // 热加载插件
new webpack.optimize.OccurrenceOrderPlugin(), // 分配执行ID
new webpack.optimize.UglifyJsPlugin(), // 压缩JS代码,开发阶段可以不配置
new ExtractTextPlugin("style.css") // 分离CSS和JS,开发阶段可以不配置
]
};
九、总结
现在我们可以来回答文章开头那三个问题了:
- 为什么会出现webpack?同类或是类似的工具有什么不好?
答:webpack的出现其实是一个模块化的过程,在开发过程中逐渐将我们的工作流程模块化,与Gulp和Grunt的工作思路不同,前者是注重模块化打包的工作,后两者是着重于开发流程的整合。 - webpack给我们带来了哪些新的思路?解决了哪些问题?
答:webpack推崇了配置项的工作方式,目的就是为了简化开发的工作流程,提高工作效率。 - 发明人对我们有什么建议,可以更好的去使用webpack?
答:其实webpack不一定是最好的。这点我们需要承认。只是我们目前能够用的和满足的工作需求让我们用webpack罢了。一样技术,不一定全部都是优点,看见优点的同时我们也需要去思考其缺点的存在。
插件体系是 webpack 的核心,事实上,webpack 的大部分功能都是通过内部插件或者第三方插件来完成的。可以说,webpack 的生态就是建立在众多插件之上的。
以一个标准的 vue-cli 生成的脚手架项目为例,一共有 7 个第三方插件以及7个内置插件。
总共 14 个插件,我们按照平均一个插件含有 2-3 个配置项(这已经是往低了算了)来计算,14 个插件就有 30 多项配置,这已经是一个现代 webpack 开发、构建使用的很基础的配置了,真实的项目只会比这个更多。
要注意到,30 多个配置项带来的复杂程度是远胜于 30 行代码的。 因为配置项已经具有了比较高的抽象性,一项配置包含的副作用是要远大于一行代码的。
现在广泛使用的这些脚手架工具,终究依赖的是 webpack,我们实际上需要的是集成度更高、封装性更高(甚至零配置)的构建工具。Vite就是一个很好的例子。