什么是webpack?
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
为什么是webpack?
市面上已经存在的模块管理和打包工具并不适合大型的项目,尤其单页面 Web 应用程序。最紧迫的原因是如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。
这些已有的模块化工具并不能很好的完成如下的目标:
- 将依赖树拆分成按需加载的块
- 初始化加载的耗时尽量少
- 各种静态资源都可以视作模块
- 将第三方库整合成模块的能力
- 可以自定义打包逻辑的能力
- 适合大项目,无论是单页还是多页的 Web 应用
webpack特点
webpack与其他模块化工具有什么区别?
-
代码拆分
Webpack 有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包。 -
Loader
Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。 -
智能解析
Webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、 AMD 还是普通的 JS 文件。甚至在加载依赖的时候,允许使用动态表达式 require("./templates/" + name + ".jade")。 -
插件系统
Webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 Webpack 插件,来满足各式各样的需求。 -
快速运行
Webpack 使用异步 I/O 和多级缓存提高运行效率,这使得 Webpack 能够以令人难以置信的速度快速增量编译。
———— 赵达《webpack中文指南》
开始
安装
npm install webpack -g
并作为开发依赖安装到本地。(确认有package.json)
npm install webpack --save-dev
之后打印webpack -h
看看是否安装成功。
需要注意的是,在webpack 3中,webpack本身和它的CLI以前都是在同一个包中,但在第4版中,他们已经将两者分开来更好地管理它们。尝试全局安装webpack-cli。
npm install -g webpack-cli
之后webpack -h
检验是否安装成功。
使用
在webpack4.0之前,可以不进行配置文件,直接使用,由于这里使用的是webpack4.0以上的版本,需要先配置文件。
新建一个webpack.config.js
文件,这是webpack的默认配置文件名。
const path = require('path');
module.exports = {
mode: "production",
entry: './a.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
-
mode:可以选择
"production" | "development" | "none"
打包模式选择,分为生产模式、开发模式、无三个选项。生产模式下输出的文件将会被压缩。 -
entry:入口起点(entry point)指示webpack应该使用哪个模块,来作为构建内部依赖图的开始。可以通过在webpack配置文件中配置entry属性,来指定一个入口起点(或多个入口起点)。默认值为
./src
。 -
output:output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件(这里我们命名为
bundle.js
),默认值为./dist
。
其中path为node.js的核心模块,path.resolve,根据给定的路径片段构造一个绝对路径,供webpack出口使用。
path.resolve(__dirname, 'dist')
表示打包后的文件将在当前目录下的dist文件下被输出,文件名为bundle.js
。
现在新建一个a.js文件。
// a.js
console.log("OK!");
运行webpack
命令。
打包成功了,打包过程中会显示日志,包含了版本号,时间等信息。并且会看到目录中多了一个dist文件夹,里面就是我们需要的打包输出文件。
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){console.log("OK!")}]);
我们使用生产模式打包,所以代码被压缩了。
现在新建一个index.html文件,引入bundle.js
,在浏览器中打开。
这就是最简单的一次webpack尝试。
打包CSS和图片
webpack本身不支持任何除JS文件之外的任何加载,我们需要下载对应的loader。
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
对于CSS文件,需要下载:
npm install css-loader style-loader --save-dev
对于图片文件,需要下载:
npm install file-loader url-loader --save-dev
首先在项目目录下创建一个css文件。
/*
./src/cstyle.css
*/
body {
background: red;
}
但是下载了loader还不行,还差一步配置loader使其生效。
const path = require('path');
module.exports = {
mode: "development",// "production" | "development" | "none"
entry: './src/js/entry.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader","css-loader"]
}]
}
};
以上配置中,对一个单独的 module
对象定义了 rules 属性,里面包含两个必须属性:test
和 use
。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在
require()或import
语句中被解析为'.css'
的路径」时,在你对它打包之前,先使用css-loader
转换一下。”
同样,对于图片文件,我们一样可以配置。
const path = require('path');
module.exports = {
mode: "development", // "production" | "development" | "none"
entry: './src/js/entry.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test:/\.(png|jpg|gif)\$/,
use: [{
loader: "url-loader",
options: {limit: 8192}
}]
}
]
}
};
url-loader:url-loader
功能类似于 file-loader
,但是在文件大小(单位 byte)低于指定的限制(这里是8kb)时,可以返回一个 DataURL。
推荐文章:data url简介及data url的利弊
现在我们来准备一次CSS和图片打包。
src文件夹为开发目录,img文件内中jerry.jpg为小于8k的jpg文件,观察如果文件大于8k会做何处理。
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>webpack 4</h1>
<div id="tom"></div>
<br>
<div id="jerry"></div>
<script type="text/javascript" src="./js/bundle.js"></script>
</body>
</html>
/* style.css */
body {
background: red;
}
#jerry {
width: 100px;
height: 100px;
background-image: url("../img/jerry.jpg");
background-repeat: no-repeat;
}
#tom {
width: 100px;
height: 100px;
background-image: url("../img/tom.jpg");
background-repeat: no-repeat;
}
webpack配置文件如下:
const path = require('path');
module.exports = {
mode: "development", // "production" | "development" | "none"
entry: './src/js/entry.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test:/\.(png|jpg|gif)\$/,
use: [{
loader: "url-loader",
options: {limit: 8192}
}]
}
]
}
};
执行webpack
可以输入webpack --display-modules
来显示打包过程中隐藏的信息。
此时,dist目录下多了一些文件。
先不管是什么,我们引入bundle.js看看效果。
可以看到,CSS文件成功打包并生效了,Jerry也出现了,并且是以Base64的形式存在在bundle.js中。
但是,tom去哪了?
打开F12检查。
404,找不到这个文件,并且发现,这个jpg文件名字变得一长串不明字串。还记得之前打包后多出来的那些文件中,也有一个jpg文件吗,这其实就是tom。
这其实也是webpack内部做的一些工作,当打包完成后,tom的文件指向不再是原来的tom.jpg,而是打包后重命名的这个长字串文件,但是问题是,index.html显然没有找到它,于是回了404。
f12也显示了,是
/dist
下的jpg,而文件是在/dist/js
下。
这在使用url-loader
时可能会出现的一个问题。
解决问题
- 既然如此,我们将index.html移动到
/dist/js
下,使jpg与html处于同层目录。
当然别忘记修改<script type="text/javascript" src="./bundle.js"></script>
中的目录。
tom就成功显示了,这是第一种也是最简单最常用的方法。
- 另一种方法,是在
webpack.config.js
输出中配置pulicPath
属性
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js',
publicPath: './js',
},
显示指明了资源获取目录。
重新编译打包后也能正常显示。
但是不推荐使用第二种方法。
在设置index.html提供资源服务时具有强制性,只要设置了字串,index.html所有的资源入口都会从这里开始,这个机制会影响之后要提到的新功能热加载工具。
热加载(自动编译打包)
利用webpack开发服务器工具:webpack-dev-server
webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
首先:npm install --save-dev webpack-dev-server
修改配置文件,告诉开发服务器(dev server),在哪里查找文件:
const path = require('path');
module.exports = {
mode: "development", // "production" | "development" | "none"
entry: './src/js/entry.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js',
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: "url-loader",
options: {
limit: 8192
}
}]
}
]
},
devServer: {
contentBase: "./dist"
}
};
如果是全局安装,那么直接运行webpack-dev-server
就可以使用了,本地安装需要在script
字段添加脚本。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open",
},
运行npm start
,不出意外,浏览器会打开一个tag,展现打包好的html。
并且,此时对原文件的任何修改,都会使webpack重新编译,重新启动服务器展现在你面前。
body {
background: greenyellow;
}
并且使用webpack-dev-server
打包在目录下输出文件,所有的编译,打包,引用都是在服务器中进行,完成。
另外,对于contentBase
字段,默认情况下,它服务于根目录下的index.html
(必须是这个文件名)。也就是说,不填写contentBase字段,默认会寻找的根目录下的index.html。
devServer: {
contentBase: "./dist"
}
使用插件
以HtmlWebpackPlugin
为例
安装:
npm install --save-dev html-webpack-plugin
npm install --save-dev clean-webpack-plugin
引入:
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
配置:
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
module.exports = {
mode: "development", // "production" | "development" | "none"
entry: './src/js/entry.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js',
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: "url-loader",
options: {
limit: 8192
}
}]
}
]
},
devServer: {
// defalut
},
plugins: [
new HtmlWebpackPlugin({template: "./index.html"}),
new CleanWebpackPlugin(["dist"])
]
};
HtmlWebpackPlugin插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包
template
字段表示指定的模版存放目录(也就是index.html的目录),在模板html中,没有引入任何js文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>webpack 4</h1>
<div id="tom"></div>
<br>
<div id="jerry"></div>
</body>
</html>
运行webpack
命令。
打包完成后发现,dist目录多了一个index.html文件,这个文件就是插件根据模版文件生成的新的html文件,并且该文件自引入了bundle.js,其他的和模板文件完全相同。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>webpack 4</h1>
<div id="tom"></div>
<br>
<div id="jerry"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
优点是,所有的静态资源与index.html都在一起,防止了一些因为打包后资源目录不正确(设置冲突)引起的资源Not found
问题。
而对于CleanWebpackPlugin
插件。
plugins: [
new HtmlWebpackPlugin({template: "./index.html"}),
new CleanWebpackPlugin(["dist"])
]
功能在于每次打包输出文件前,会事先将dist
目录下的所有文件删除一次后在输出此次打包的文件,因为会有一些冗余文件存在而造成影响。
阮一峰webpack教程集
webpack快速入门——如何安装webpack及注意事项
webpack 3 零基础入门教程 #16 - 使用 ProvidePlugin 插件来处理像 jQuery 这样的第三方包
Webpack飞行手册
网友评论