webpack 是什么
web开发中常用到的静态资源主要有JavaScript、CSS、图片、pug等文件,webpack中将静态资源文件称之为模块。webpack是一个模块打包工具,其可以兼容多种js书写规范,且可以处理模块间的依赖关系,具有更强大的js模块化的功能。 官方网站中用下图清晰的描述了webpack采用不同的loader加载不同的资源文件,打包生成多个js文件,也可以根据设置生成独立的图片、css文件等。
为什么用webpack
在以往的开发过程中,经常会遇到以下三种情况:
- 项目中资源多样性和依赖性 - js、css、png、less、pug等为了方便开发,我们经常会使用不同的语法来编写文档,用less、sass、pug等会提高开发效率,但同时我们需要借助gulp或grunt来编写任务编译文件或对图片进行压缩等。
- JS模块规范复杂化 - AMD、CommonJS、ES6等 requireJS主要用来处理AMD规范的JS文件,若使用CommonJS规范的JS库文件,需进行AMD规范的封装,才能正常使用。而browserify主要处理CommonJS规范的文件,其他规范也需要进行转化。近期ES6的兴起,前面两种打包工具已经不能满足我们的需求了。
- 开发与线上文件不一致性(打包压缩造成影响)
webpack可以很好地解决上面的问题,它具有Grunt、Gulp对于静态资源自动化构建的能力,是一个出色的前端自动化构建工具、模块化工具、资源管理工具。
webpack 特性
webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:
- 对 CommonJS 、 AMD 、ES6的语法做了兼容
- 对js、css、图片等资源文件都支持打包
- 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、TypeScript、ES6的支持
- 有独立的配置文件webpack.config.js
- 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
- 支持 SourceUrls 和 SourceMaps,易于调试
- 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活
- webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快
webpack 安装及使用
webpack 可以作为全局的npm模块安装,也可以在当前项目中安装。(首先要有node环境)1
2
3
4
5
6
7mkdir webpack-demo //创建项目文件夹
cd webpack-demo
// 创建 package.json,这里会问一些问题,直接回车跳过就行
npm init
// 推荐这个安装方式,当然你也安装在全局环境下
// 这种安装方式会将 webpack 放入 devDependencies 依赖中
npm install --save-dev webpack
webpack的使用通常有三种方式:
- 命令行使用:webpack
其中entry.js是入口文件,bundle.js是打包后的输出文件 node.js API使用:
1
2
3
4var webpack = require('webpack');
webpack({
//configuration
}, function(err, stats){});默认使用当前目录的webpack.config.js作为配置文件。如果要指定另外的配置文件,可以执行:webpack –config webpack.custom.config.js
webpack 常用命令
webpack的使用和browserify有些类似,下面列举几个常用命令:
- webpack 最基本的启动webpack命令进行打包
- webpack -w 提供watch方法,实时进行打包更新
- webpack -p 对打包后的文件进行压缩
- webpack -d 提供SourceMaps,方便调试
- webpack –colors 输出结果带彩色,比如:会用红色显示耗时较长的步骤
- webpack –profile 输出性能数据,可以看到每一步的耗时
- webpack –display-modules 默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块
- webpack –progress 显示打包进度
前面的四个命令比较基础,使用频率会比较大,后面的命令主要是用来定位打包时间较长的原因,方便改进配置文件,提高打包效率。
webpack 配置文件
项目中静态资源文件较多,使用配置文件进行打包会方便很多。最简单的Webpack配置文件webpack.config.js如下所示:1
2
3
4
5
6
7
8
9
10
11module.exports = {
entry:[
'./entry.js',
...
],
output: {
path: __dirname + '/output/',
publicPath: "/output/",
filename: 'bundle.js'
}
};
- 其中entry参数定义了打包后的入口文件,数组中的所有文件会打包生成一个filename文件
- output参数定义了输出文件的位置及名字,其中参数path是指文件的绝对路径,publicPath是指访问路径,filename是指输出的文件名。
webpack Loader(模块加载器)
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不同模块的加载是通过模块加载器(webpack-loader)来统一管理的。loaders之间是可以串联的,一个加载器的输出可以作为下一个加载器的输入,最终返回到JavaScript上。loader的配置可以写在配置文件中,通过正则表达式的方式对文件进行匹配,具体可参见下面的示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37module: {
rules: [
{
test: /\.js$/, // js文件后缀
loader: 'babel-loader', //使用babel-loader处理
include: [resolve('src'), resolve('test')] //必须处理包含src和test文件夹
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, //图片后缀
loader: 'url-loader', //使用url-loader处理
options: { // query是对loader做额外的选项配置
limit: 10000, //图片小于10000字节时以base64的方式引用
name: utils.assetsPath('img/[name].[hash:7].[ext]') //文件名为name.7位hash值.拓展名
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, //字体文件
loader: 'url-loader', //使用url-loader处理
options: {
limit: 10000, //字体文件小于1000字节的时候处理方式
name: utils.assetsPath('fonts/[name].[hash:7].[ext]') //文件名为name.7位hash值.拓展名
}
},
{
test: /\.css$/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
}
]
},
]
}
以上loader可以通过npm安装:1
$ npm install xxx-loader --save-dev //xxx是你要安装的loader名字,如处理转义es6的babel:npm install babel-loader --save-dev
Bable
Babel 可以让你使用 ES2015/16/17 写代码而不用顾忌浏览器的问题,Babel 可以帮你转换代码。
处理图片
url-loader 在options选项设置限制你的图片大小,小于限制会将图片转换为 base64格式
处理 CSS 文件
css-loader 和 style-loader 库。前者可以让 CSS 文件也支持import,并且会解析 CSS 文件,后者可以将解析出来的 CSS 通过标签的形式插入到 HTML 中,所以后面依赖前者。
但是将 CSS 代码整合进 JS 文件也是有弊端的,大量的 CSS 代码会造成 JS 文件的大小变大,操作 DOM 也会造成性能上的问题,所以接下来我们将使用 extract-text-webpack-plugin 插件将 CSS 文件打包为一个单独文件
首先安装1
npm i --save-dev extract-text-webpack-plugin
然后修改 webpack.config.js 代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29const ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// ....
module: {
rules: [
{
test: /\.css$/,
// 写法和之前基本一致
loader: ExtractTextPlugin.extract({
// 必须这样写,否则会报错
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
modules: true
}
}]
})
]
}
]
},
// 插件列表
plugins: [
// 输出的文件路径
new ExtractTextPlugin("css/[name].[hash].css")
]
}
常用插件
有时候项目你会发现这个 bundle.js 很大,这肯定是不能接受的,所以要用插件优化项目。
CommonsChunkPlugin
抽离多个entry的公共模块
1
2
3
4
5
6
7
8
9
10
11
12
13new webpack.optimize.CommonsChunkPlugin({
name: "commons",
// (the commons chunk name)
filename: "commons.js",
// (the filename of the commons chunk)
// minChunks: 3,
// (Modules must be shared between 3 entries)
// chunks: ["pageA", "pageB"],
// (Only use these entries)
})抽离vendor模块(第三方库)
1
2
3
4
5
6
7
8
9
10
11
12
13
14entry: {
vendor: ["jquery", "other-lib"],
app: "./entry"
}
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
// filename: "vendor.js"
// (Give the chunk a different name)
minChunks: Infinity,
// (with more entries, this ensures that no other module
// goes into the vendor chunk)
})抽离子模块中的公共模块到父模块中,会增加首屏加载的时间
1
2
3
4
5
6
7
8
9
10new webpack.optimize.CommonsChunkPlugin({
// names: ["app", "subPageA"]
// (choose the chunks, or omit for all chunks)
children: true,
// (select all children of chosen chunks)
// minChunks: 3,
// (3 children must share the module before it's moved)
})和3类似,不过不是抽离到父模块,而且额外抽离出一个异步的公共模块
1
2
3
4
5
6
7
8
9
10
11
12
13new webpack.optimize.CommonsChunkPlugin({
// names: ["app", "subPageA"]
// (choose the chunks, or omit for all chunks)
children: true,
// (use all children of the chunk)
async: true,
// (create an async commons chunk)
// minChunks: 3,
// (3 children must share the module before it's separated)
})
html-webpack-plugin
//如果一个html文件需要引入很多 js 文件,每次都通过script标签手动加入html中很麻烦,
通过此插件 build 操作会发现同时生成了 HTML 文件,并且已经自动引入了 JS 文件1
2
3
4
5
6
7
8module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
template: 'index.html'
})
]
};
extract-text-webpack-plugin
//提取样式到单独的css文件1
new ExtractTextPlugin("css/[name].[contenthash].css"),
UglifyJsPlugin
//压缩混淆插件 (可以通过webpack.optimize.UglifyJsPlugin使用)1
2
3
4
5
6// 压缩 JS 代码
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
ProvidePlugin
//打包时一次性引入项目中所有依赖库,例如jquery等1
2
3
4
5new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
//这样$函数会自动添加到当前模块的上下文,无需显示声明
DefinePlugin
//决定打成dev包还是production包会用到它1
2
3new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("process.env.NODE_ENV")
})
clean-webpack-plugin
在每次生成dist目录前,先删除本地的dist文件,特别是是带有hash值的js文件(每次手动删除太麻烦)1
new CleanWebpackPlugin(['dist']), //传入数组,指定要删除的目录