Webpack 是什么
webpack是一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含less/sass)、图片等都作为模块来使用和处理。
简单说就是模块加载器,通过使用Webpack,能够像Node.js一样处理依赖关系,然后解析出模块之间的依赖,将代码打包。
为什么需要打包?
像sass,JSX等代码虽然极大的提高了开发效率,但是本身并不被浏览器所识别,需要我们对其进行编译和打包,变成浏览器识别的代码
模块化(让我们可以把复杂的代码细化为小的文件)
优化加载速度(压缩和合并代码来提高加载速度,压缩可以减少文件体积,代码合并可以减少http请求)
使用新的开发模式
webpack主要特性如下:
同时支持CommonJS和AMD模块(对于新项目,推荐直接使用CommonJS);
串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持;
可以基于配置或者智能分析打包成多个文件,实现公共模块或者按需加载;
支持对CSS,图片等资源进行打包,从而无需借助Grunt或Gulp(browserify只能打包JS文件);
开发时在内存中完成打包,性能更快,完全可以支持开发过程的实时打包需求;
对source map有很好的支持。
Source map(使调试更容易)
Source map就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,这将给开发者带来了很大方便。
详见传送门
安装 Webpack
首先得安装Node.js
全局环境下安装,在任意目录,输入以下命令
//这是为了能够在全局环境中使用webpack命令
//非全局安装需使用node_modules/.bin/webpack
$ npm install webpack -g
1
2
3
仅在项目在中安装,切换到项目根目录,输入以下命令
$ npm install webpack --save-dev
1
检查是否安装成功,显示如下:
$ webpack -v
1
更快捷的执行打包任务
{
"name": "demo",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "webpack-dev-server --progress --colors --hot --inline -d",
"build": "webpack --progress --colors --minify"
},
"license": "ISC",
"dependencies": {
},
"devDependencies": {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
执行类似于node_modules/.bin/webpack这样的命令不仅繁琐还容易出错,不过值得庆幸的是npm可以引导任务执行,对其进行配置后可以使用简单的npm start或者npm run build命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如上面的配置所示。
**注意:**npm的start是一个特殊的脚本名称,它的特殊性在于:在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}如上面例子中的npm run build。
**特别说明:**package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。
配置
每个项目下都必须配置有一个 webpack.config.js ,它的作用如同常规的 gulpfile.js/Gruntfile.js ,就是一个配置项,告诉 webpack 它需要做什么。
webpack.config.js配置详解
这里给出我自己的配置:
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
noParse:[/jquery/],//表示跳过jquery,不对其进行编译,这样可以提高打包的速度
plugins:[
commonsPlugin,
//配置了NoErrorsPlugin插件,用来跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误
new webpack.NoErrorsPlugin()
],
entry: {
index:'./src/js/startup.js'
},
//入口文件输出配置
output:{
//path: 打包好的资源存放的位置
path:'./js',
//filename: 打包后的文件名
filename: '[name].bundle.js'
},
// externals: { //排除构建文件外
// 'react': 'React'
// },
module: {
//加载器配置
//module.loaders 是最关键的一块配置。它告知 webpack 每一种文件都需要使用什么加载器来处理
loaders:[
{
//用于匹配加载器支持的文件格式的正则表达式
test: /\.css$/,
//多个加载器通过"!"连接,加载器是从右向左开始使用的
//loader指定了要使用的加载器类型
loader: 'style-loader!css-loader'
},
{
//配置信息的参数“?limit=8192”表示将所有小于8kb的图片都转为base64形式 (其实应该说超过8kb的才使用 url-loader 来映射到文件,否则转为data url形式)
test: /\.(png|jpg)$/,
//加载器支持通过查询字符串的方式接收参数
loader: 'url-loader?limit=8192'
},
{
//presets:指定哪些代码转换器将启用babel
test: /\.js$/,
loader: 'babel-loader',
//exclude:/node_modules/,排除node_modules这个文件夹中的代码
//include:/(admin|consumer)//表示只针对这两个文件夹中的代码进行打包
//include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)
exclude:/node_modules/,
query:{
presets:['es2015','react']
}
},
{
test: /\.jsx$/,
//?表示加载器支持通过查询字符串的方式接收参数
//!表示多个加载器之间通过"!"连接
loader: 'babel-loader!jsx-loader?harmony'
}
]
},
//文件解析配置
//会依次寻找不带后缀的文件,.js后缀文件以及.jsx后缀文件。先找到哪个是哪个
resolve: {
extensions: ['', '.js', '.jsx'],//当requrie的模块找不到时,添加这些后缀
root:'E:/myev/src',//绝对路径
alias: {//别名配置
}
}
}
}
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
这里对Webpack的打包行为做了配置,主要分为几个部分:
plugins 是插件项(是一个数组):这里我们使用了一个 CommonsChunkPlugin 的插件,它用于提取多个入口文件的公共脚本部分,然后生成一个 common.js 来方便多页面之间进行复用。对于用到的插件,只需要将插件new出来放到数组中即可。
entry:指定打包的入口文件,每有一个键值对,就是一个入口文件
output:配置打包结果,path定义了输出的文件夹(即打包好的文件要放到哪里),filename则定义了打包结果文件的名称,filename里面的[name]会由entry中的键(这里是index)替换。publicPath:如果要使用webpack-dev-server(webpack配置服务器,端口号是8080)就需要配置(标明我们要从哪个地方获取打包好的文件)
externals:当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题。
resolve:定义了解析模块路径时的配置,常用的就是extensions,可以用来指定模块的后缀,这样在引入模块时就不需要写后缀了,会自动补全。比如 Hello.jsx 这样的文件就可以直接用 require(./Hello) 引用。 resolve 配置,配置查找模块的路径和扩展名和别名(方便书写)。可以用 alias 指定从非npm引入的库,如
jquery:path.resolve(__dirname,'./bower_components/jquery/dist/jquery.js') ,
这样在 import 时可以替换引用路径。
- module:定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理,这正是webpack强大的原因。比如这里定义了凡是.js结尾的文件都是用babel-loader做处理,而.jsx结尾的文件会先经过babel-loader处理,然后经过jsx-loader处理。当然这些loader也需要通过npm install安装,“-loader”其实是可以省略不写的,多个loader之间用“!”连接起来。
Webpack还有很多其他的配置,具体可以参照它的配置文档
对应各种不同文件类型的资源, Webpack 有对应的模块 loader。比如 CoffeeScript 用的是 coffee-loader, 其他还有很多。请参考相关文档
在 Webpack 当中, 所有的资源都被当作是模块, js, css, 图片等等。因此, Webpack 当中 js 可以引用 css, css 中可以嵌入图片 dataUrl。
Webpack的加载器支持参数,jsx-loader就可以添加?harmony参数使其支持ES6语法。为了让Webpack识别什么样的资源应该用什么加载器去载入,需要在配置文件进行配置:通过正则表达式对文件名进行匹配。
加载器之间的级联是通过感叹号来连接,例如对于LESS资源,写法为style-loader!css-loader!less-loader。从右向左开始使用,less->转为css字符串->使用style将代码放到页面style标签中。
css-loader——将样式打包成字符串
style-load——将样式字符串添加到页面的style标签中
对于小型的图片资源,也可以将其进行统一打包,由url-loader实现,代码中url-loader?limit=8192含义就是对于所有小于8192字节的图片资源也进行打包。这在一定程度上可以替代Css Sprites方案,用于减少对于小图片资源的HTTP请求数量。
执行打包
如果通过npm install -g webpack方式安装webpack的话,可以通过命令行直接执行打包命令,比如:
$ webpack –config webpack.config.js
这样就会读取当前目录下的webpack.config.js作为配置文件执行打包操作
常用webpack命令:
在开发环境构建一次
webpack
构建并生成源代码映射文件
webpack -d
在生成环境构建,压缩、混淆代码,并移除无用代码
webpack -p
快速增量构建,可以和其他选项一起使用
webpack –watch
progress 显示打包过程中的进度,colors打包信息带有颜色显示
webpack –progress –colors
理解文件路径
require(‘lodash’) // 从模块目录查找
require(‘./file’) // 按相对路径查找
CSS 及图片的引用
require(‘./bootstrap.css’);
require(‘./myapp.less’);
var img = document.createElement(‘img’);
img.src = require(‘./glyph.png’);
一个模块需要用它的文件路径来加载,看一下下面的这个结构:
–app
——modules
———–MyModule.js
——main.js (entry point,入口文件)
——utils.js
打开 main.js 然后可以通过下面两种方式引入 app/modules/MyModule.js
app/main.js
// ES6
import MyModule from ‘./modules/MyModule.js’;
// CommonJS
var MyModule = require(‘./modules/MyModule.js’);
最开始的 ./ 是 “相对当前文件路径”
让我们打开 MyModule.js 然后引入 app/utils:
app/modules/MyModule.js
// ES6 相对路径
import utils from ‘./../utils.js’;
// ES6 绝对路径
import utils from ‘/utils.js’;
// CommonJS 相对路径
var utils = require(‘./../utils.js’);
// CommonJS 绝对路径
var utils = require(‘/utils.js’);
相对路径是相对当前目录。绝对路径是相对入口文件,这个案例中是 main.js。
CommonChunks插件
Common Chunks 插件的作用:提取代码中的公共模块,然后将公共模块打包到一个独立的文件中去,以便在其它的入口和模块中使用。
我们在配置项中添加CommonChunksLoader的配置,使用它来提取公共模块。
var webpack = require('webpack');
var config = {
entry:{
main1:'./main1',
main2:'./main2'
},
output:{
filename:'bundle.[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('common.js', ['main1', 'main2'])
]
};
module.exports = config;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在配置文件中添加了webpack的引用(同时也要在项目目录下安装webpack),然后添加了plugins选项,引用了webpack.optimize.CommonsChunkPlugin来提取公共模块,参数common.js表示公共模块的文件名,后面的数组元素与entry一一对应,表示要提取这些模块中的公共模块。
重新使用webpack打包后,看到生成的文件中多了一个common.js
这个common.js正是公共部分a和b两个模块。而生成的bundle.main1.js中只包含了c模块,bundle.main2.js中则没有包含任何其它模块。
具体代码详见我的github:传送门
最后要在HTML文件中引入公共部分common.js。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>CommonsChunkPlugin</title>
</head>
<body>
<script type="text/javascript" src="common.js"></script>
<script type="text/javascript" src="bundle.main1.js"></script>
</body>
</html>