Webpack目前官方发布的最新版本是3.1.0,相对于2.0的怎么本,在语法上没有变动,只是新增了功能。使用webpack,需要事先安装node.js,并对node.js生态有一些基本的了解,比如(npm init 初始化项目,npm install 安装一个包等等)。使用webpack通常有两种方式:1. 命令行方式(CLI) , 2 script方式(推荐)。 两种方式都需要理解webpack概念。
概念
webpack is a module bundler for modern JavaScript applications. When webpack processes your application, it recursively builds a dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles - often only one - to be loaded by the browser.
webpack是时下很火的js模块化的打包工具。当用webpack处理你的应用时,它会递归的构建每个模块的依赖关系图,然后把所有的这些依赖模块打包到一个由数个小块组成的文件中--通常只有一个(将被浏览器加载)
It is incredibly configurable, but to get started you only need to understand Four Core Concepts: entry, output, loaders, and plugins.
虽然它是高度可配置的,但是初步使用你只需要理解下面这四个核心概念:入口,输出,加载器,插件。默认情况下,所有这些配置项,都保存在项目根目录下一个名为webpack.config.js的文件中。
一. 入口 (entry)
The entry point tells webpack where to start and follows the graph of dependencies to know what to bundle. You can think of your application's entry point as the contextual root or the first file to kick off your app.
所谓的入口,就是告诉webpack从哪里开始并通过依赖关系图得知要哪些文件要打包。你可以理解为应用程序最先读取的文件。读取这个文件之后,就会产生一个模块依赖关系图,那么当前这个文件,就是依赖关系的根。
webpack.config.js
1
2
3
|
module.exports = {
entry:
'./path/to/my/entry/file.js'
};
|
上例告诉webpack从./path/to/my/entry/file.js开始打包,这是最简单的一种入口形式,实际上,它是下面这种形式的简写
1
2
3
4
5
|
module.exports = {
entry : {
main :
'./path/to/my/entry/file.js'
}
}
|
entry 还可以是数组的方式
1
2
3
|
module.exports = {
entry: [
'./path/to/my/entry/file.js'
]
};
|
这对于不想在js中引入css的情况非常有用,比如:
1
2
3
|
module.exports = {
entry: [
'./path/to/my/entry/file.js'
,
'./path/to/my/entry/file.css'
]
};
|
如果是多页应用或者想要提取公共代码(CommonsChunkPlugin)的话,就需要使用下面这种入口方式:
1
2
3
4
5
6
7
|
module.exports = {
entry: {
pageOne:
'./src/pageOne/index.js'
,
pageTwo:
'./src/pageTwo/index.js'
,
pageThree:
'./src/pageThree/index.js'
}
}
|
当然,这里也可以使用数组的形式,同时引入样式或其它文件
1
2
3
4
5
6
7
|
module.exports = {
entry: {
pageOne: [
'./src/pageOne/index.js'
,
'./src/pageOne/index.css'
],
pageTwo: [
'./src/pageTwo/index.js'
],
pageThree: [
'./src/pageThree/index.js'
]
}
}
|
以上就是关于入口这个概念和配置的全部内容了,更详细的介绍请移步官方网站关于入口的扩展介绍 。 除了要告诉webpack要从哪个文件开始打包之外,还要告诉它打包生成的文件存放在哪个位置。于是就有了输出的配置。
二. 输出 (output)
webpack的output属性就是告诉webpack怎么对待这些打包的代码。
Configuring the output
configuration options tell webpack how to write the compiled files to disk. Note that, while there can be multiple entry
points, only one output
configuration is specified.
配置output属性就是告诉webpack把编译后的文件写在磁盘的哪个地方。注意一点,虽然入口中可以配置成多个文件的形式,但是输出只能指定一个。
--- 打包之后资源,并不总是写在磁盘上,也可能是在内存中,比如使用webpack-dev-server的时候。(译者注)
webpack.config.js
1
2
3
4
5
6
7
8
9
|
const path = require(
'path'
);
module.exports = {
entry:
'./path/to/my/entry/file.js'
,
output: {
path: path.resolve(__dirname,
'dist'
),
filename:
'my-first-webpack.bundle.js'
}
};
|
output有两个属性是必须的。
1. path 输出路径
- An absolute
path
to your preferred output directory. 通常是绝对路径。
2. filename 文件名
当入口是多个文件的时候,可以用变量[name]代替固定的名字。这里的name变量,对应entry中的key. ( 对于entry:配置成字符串的时候,输出的文件名就是main,如果不明白,查看前面入口是谁的简写形式)
1
2
3
4
5
6
7
8
9
10
11
|
module.exports = {
entry: {
app:
'./src/app.js'
,
search:
'./src/search.js'
},
output: {
filename:
'[name].js'
,
path: __dirname +
'/dist'
}
}
|
在filename属性中,除了[name]之外,还可以用[hash],[contenthash]等内置变量, Tips ! [hash:8] 截取hash前8位
3. publicPath 虚拟路径
如果你有一个域名或打算用CDN,可以使用这个配置,如果你不知道,或暂时不打算用,可以先留空
1
2
3
4
5
|
output: {
path:
"/home/proj/cdn/assets/"
,
publicPath:
"http://cdn.example.com/assets/"
,
filename:
'[name].js'
}
|
注意:如果配置了publicPath属性,那么本地资源路径,都会被替换成publicPath所指定的地址 (译者注)
三、加载器
Loaders are transformations that are applied on the source code of a module. They allow you to pre-process files as you import
or “load” them. Thus, loaders are kind of like “tasks” in other build tools, and provide a powerful way to handle front-end build steps. Loaders can transform files from a different language (like TypeScript) to JavaScript, or inline images as data URLs. Loaders even allow you to do things like import
CSS files directly from your JavaScript modules!
加载器用于将资源代码转换成模块。它允许您在导入或“加载”它们时预处理文件。因此,加载器就像处理各种任务的小工具,它提供一个强大的方法来处理前端构建步骤。加载器可以把不同类型的文件(比如typescript)转换成javascript 或者把图片处理成内联的数据地址(如base64),加载器甚至可以像导入js模块一样直接导入样式文件。
加载器听起来很历害的样子,不过它不能算是webpack的功能,如果要想在项目中导入样式模块,需要先安装样式的加载器(css-loader), 同样的,要导入图片要安装文件加载器(file-loader)
在命令行中输入:
npm install --save-dev css-loader
即可安装样式加载器,后面跟的参数--save-dev 表示将保存到package.json的开发环境依赖属性中。对应还有一个--save表示安装到生产环境。 package.json文件可以通过手工创建,也可用npm init(推荐)
创建。更多关于package.json的介绍请移步这里
webpack.config.js
1
2
3
4
5
6
7
|
module.exports = {
module: {
rules: [
{ test: /\.css$/, use:
'css-loader'
}
]
}
};
|
加载器位于module属性中(适用于webpack2.0+), rules 是一个数组,里边可以添加多个加载器(loader). 每一个加载器用对象的方式组织,下面是一个更为复杂的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader:
'style-loader'
},
{
loader:
'css-loader'
,
options: {
modules:
true
}
}
]
}
]
}
|
每一个加载器,都有各自对应的配置内容,比如css-loader的详细配置请移步这里 ,但是不管怎么说,方法都是一样的。需要指出的事,加载器,除了在webpack.config.js中配置之外,还可以直接在js代码中用行内加载的方式使用,比如
import Styles from 'style-loader!css-loader?modules!./styles.css'; //多个加载器之间用!进行分隔
不过这种方式写起来很冗长,不推荐。还有一种方式,也一并提一下,在CLI方式中的使用
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
同样的冗长,了解一下即可,实际项目中,大多是使用webpack.config.js进行集中配置。
一般来说,一个加载器就可以完成一个任务,比如file-loader 可以加载文件到js的模块中,可是我们常常发现,加载css到js模块中,除了使用css-loader之外,常常还有一个多个附加的伴侣,比如这个style-loader。
这是为什么呢? 这其实是因为样式的使用有两种方式,一种是内联的方式,写在html的style标签中,还有一种是外链,通过link的方式引用。而我们在开发环境中,css是作为js的模块打包进js文件中的。而且js要使用样式,必须在html中插入style标签,然后把样式以字符串的形式插入标签内。所以这里需要用到两个加载器,首先用css-loader把样式读取到js的模块中,然用style-loader写入style标签中。所以也就出现了css-loader和style-loader成对使用的情况。对于同时使用多个加载器的情况,它们的调用顺序是从右向左的。也就是说最先使用的要写在最右边,对于新式这个例子来说,就是style-loader!css-loader这样写。关于style-loader这个加载器,其实它还有一个少被人提起,却非常有用的功能--热替换,这部分内容后面再细说
有人不禁要问,既然css可以直接用link的方式使用,为什么不把css打包成独立的css文件呢?这样我就不需要用style-loader了,而且把样式内联在html中,看着不舒服。好吧,这就不得不引出webpack中的第四个概念了---插件
四、 插件 plugins
They also serve the purpose of doing anything else that a loader cannot do.
插件用于完成一些载器所不能做的事情。
其实这样解释不并是很严谨,但是插件,确实使的webpack更加强大了。回到前说的独立样式的问题,结合插件来做。
先安装extract-text-webpack-plugin 这个插件
npm install --save-dev extract-text-webpack-plugin
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
|
var
path = require(
'path'
);
var
webpack = require(
'webpack'
);
var
ExtractTextPlugin = require(
'extract-text-webpack-plugin'
);
var
node_modules_dir = path.resolve(__dirname,
'../node_modules'
);
module.exports = {
entry: {
home:
'./src/home.es6'
},
module: {
rules:[{
test:/\.css/,
use: ExtractTextPlugin.extract({
fallback:
"style-loader"
,
use: [
"css-loader"
,
"postcss-loader"
]
})
}
]
},
output: {
path: path.resolve(__dirname,
"../dist"
),
filename:
'js/[name][hash:7].js'
},
plugins: [
new
ExtractTextPlugin({
filename:
"css/[name].[contenthash].css"
}),
new
webpack.optimize.UglifyJsPlugin()
],
}
|
new webpack.optimize.UglifyJsPlugin() //用来压缩代码,这个是webpack送的,不需要单独安装, new ExtractTextPlugin 用来提取css到一个独立的样式文件,这个插件需要自己安装,还要在loader中进行相应的改动,配合使用才能生效。值得注意的是,这样一来,就牲牺了css的热替换能力了。不过没有关系,这个插件一般用在生产环境。
Hot Module Replacement (HMR热替换)
这应当属于webpack中比较高级的内容了,但是在开发中,结合热替换一起使用,会很方便。最简单的使用方式是通过 webpack-dev-server --hot --inline
关于这一部分的内容,参考我以前写的一篇文章。官方介绍移步这里这里需要指出的是,并不是加了--hot就可以实现热替换,这需要对应的加载器支持“热替换”才行的。
比如前面说的样式,style-loader是可以支持热替换的。而且对于js来说,虽然webpack不需要额外的加载器就可以进模块化,但是并不支持“热替换", 如果使用react.js的话,可以用react-hot-loader,如果用Vue的话,可以用vue-loader。 如果该加载器不支持热替,会降级为自动刷新。因此,热替换,并不是自动刷新哦。
总结
webpack3还比较新,如果之前使用webpack2的项目,直接升级到webpack3的话,需要小心了,有些加载器和插件可能不支持webpack3. 比如提取css为独立文件的插件,就不支持。webpack-dev-server也不支持 (至少在我写这篇文章的时候还是不支持的),但是不能因噎废食,新技术最终会取替旧技术,这是行业趋势。安于现状,只会被淘汰。webpack的学习最好是自己写一个简单项目,把文章提到的和自己学到的loader,plugin都试试。把基本的概念和用法型明白之后,可以安装别人写的比较优盘的项目拿来对照学习,比如Vue-cli。看看行业大牛们,是怎么配置webpack的,他们都用了哪些插件和加载器。最后,我也提供一下自己练习的项目,做为本篇的结尾。https://github.com/bjtqti/font-end-boilerplate.git