webpack
本文有些内容会前后穿插,需仔细阅读 loader 和 plugin 部分。
说到前端自动化工具,我们总会想到它的这些功能:
编译
将sass、less这类css预处理语言,编译成浏览器可识别的css,如: sass less -> css
将es6,es7这类下一带JavaScript标准或者react的jsx模板,编译成浏览器可识别的 es5,如:es6,es7,coffee,ts,jsx -> es5
将各类html引擎模板,编译成浏览器可识别的普通html页面,如:jade ejs -> html
服务搭建(引用包),文件监听(组件热更新)
现在前端工作基本都采用前后端分离的模式,很多项目的开发都基于 ajax 请求数据。
因此,在项目开发时,你需要一个运行服务平台,而很多自动化工具都内置服务,倘若没有内置服务,你也可以安装相应的模块。
另外,你还可以借助相关插件插件,来实现文件监听的功能,当项目下的文件被修改,浏览器会自动刷新。
资源压缩、合并以及打包
项目开发完,发布上线前,为减少单个文件的大小,你需要对文件代码进行压缩。为减少页面请求数,你需要合并两个或多个js或css文件,即: css,js 压缩、合并
而当修改了某个文件时,为避免浏览器缓存,每次打包需要更新修改文件的后缀名(这种后缀一般通过md5)、或者对文件重命名,即: 更新文件的md5、或重命名
对于一些特殊的静态资源,比如说图片,小尺寸的图我们希望以 data url
的形式嵌入到页面HTML中,进一步减少请求数。而大图则使用普通的引入方式,即:图片优化
通常而言,我们项目有这么两个目录,src
和 dist
(可能有其他命名),分别表示开发目录、打包目录。当开发完成,我们将 src
下的文件打包处理后,塞进 dist
目录。即:src -> dist等 文件与目录变化
和其他前端自动化工具一样,webpack 也具备了上述功能。
webpack 的理念是将所有的资源,无论是图片、还是页面、又或者 css、js,都把它们当成模块来处理。来看看它是怎么做的,首先安装它:
以下为全局安装:
> npm install -g webpack |
通过 webpack -h
查看相关信息。
一、基本操作
新建项目目录,然后 npm init
,生成对应的 package.json
文件。
针对单个项目,再进行本地安装,将依赖包信息写入配置文件:
> npm install --save-dev webpack |
一般而言,项目都有两个基本文件夹,即 src
和 dist
,开发目录 和 打包目录。
我们在项目根目录创建这两个文件夹,接着,在 src
文件里,新建 index.html
和 main.js
文件。
<!-- index.html --> |
// main.js |
从上面的代码,我们可以看到,页面引用了 bundle.js
这个文件,它并非我们新建的,而是后面由webpack将 main.js
打包后的 JavaScript 文件。
要将 main.js
打包成 bundle.js
。只要通过以下命令:
> webpack src/main.js dist/bundle.js |
注意:如果提示 bash: webpack: command not found
,你可能没全局安装 webpack。如果你实在不想全局安装也行,但需要通过如下命令:
> node_modules/.bin/webpack src/main.js dist/bundle.js |
编译完成后,在 dist
目录里会新生成 bundle.js
文件,直接打开 index.html
页面,你会在页面里看到 hello webpack!
。
因为 webpack 被称之为 模块打包器,所以,接着用它来处理模块。我们希望把 main.js
里的文字信息抽离出来,独立成一个模块。
于是,我们在 src
文件夹里新建 text.js
文件:
// text.js |
而 main.js
也更新如下:
// main.js |
再次运行 webpack src/main.js dist/bundle.js
,刷新 index.html
页面,你会发现里面的内容仍然可以正常显示。
上面用到了 webpack 最基本的命令:
> webpack <entry> <output> |
具体可详见:webpacl-cli
除了这个命令外,还有其他几个常用的命令:
命令 | 说明 |
---|---|
webpack <entry> [<entry>] <output> |
启动 |
webpack --config |
默认是指定 webpack.config.js,你可指定其它的配置文件 |
webpack -watch 或 webpack -w |
观察文件系统的变化,不用输入 webpack 去重新编译 |
webpack -p |
打包并压缩文件 |
webpack -d |
打开SourceMap调试代码 |
webpack -colors |
开启/关闭控制台的颜色 |
webpack -profile |
查看每一步耗时 |
通过上面的命令表,我们可以知道,如果要打包后的 bundle.js
是压缩过的,只需要如下命令:
> webpack -p src/main.js dist/bundle.js |
二、webpack.config.js
当我们每次开发完项目,打包每次都需要在命令行里输入类似如下命令:
> webpack -p src/main.js dist/bundle.js |
打包东西少还好,但一旦内容多了起来,是否会觉得繁琐,而且也有诸多不便?还好,webpack 早就为你考虑好了,默认情况下,我们可以在项目的根目录,新建一个叫 webpack.config.js
的配置文件,因为 webpack 把所有的文件都当成模块,因此,这个配置文件,你也可以把它理解为一个模块。
// webpack.config.js |
都说要用好 webpack,关键在于配置文件,webpack 会依照配置文件的内容进行打包处理。而配置文件又涉及到四个概念,即:入口文件(Entry)、出口文件(Output)、加载器(loader)、插件(plugin)。
这意味着,要操作 webpack,理解这四个概念非常重要。
当新增完配置文件,你在命令行中,只需要输入如下命令,便可完成打包:
> webpack -p |
如果我们希望打包的文件名保持不变,可以使用 webpack 中提供的关键词 [name]
:
// webpack.config.js |
此时,打包后在 dist
目录便会生成 main.js
文件。
前面文章提到了,为了解决缓存问题,自动化工具都会给有修改过的文件应用 md5 后缀机制。webpack 也不例外,它还提供了两种方案,即: [hash]
与 [chunkhash]
。
配置文件可修改为:
// webpack.config.js |
或
// webpack.config.js |
[hash]
是 webpack 每次打包编译后的版本号,通过下面的界面(假设打包main.js 和 page.js 这两个文件),可以看出,打包后的文件,都是采用相同的 md5 后缀。

这就导致了一个问题,如果有些文件没有修改,而 md5 却改变了,这会使得这些文件在浏览器的缓存失效。
因此,你需要用到 [chunkhash]
。
[chunkhash]
则是基于模块内容计算出的hash值,是针对单个模块。每次打包后,只有修改的文件,才更新md5后缀,下图是我修改 page.js 前后编译打包的对比结果:

可以看到,只要有文件发生变化,webpack 每次打包后版本号都不一样,另外,修改的 page.js 的md5也发生了改变。
不过,主要注意的是,因为打包后文件名发生了变化,此时页面里引用的 js 文件就不再是打包后的文件了,怎么让页面自动引用打包的文件呢?文章后面会谈到,可通过插件解决。
[hash] 与 [chunkhash] 的异同
[hash]
与[chunkhash]
默认都是20位字符串,你可以手动设置位数,如8位:[hash:8]
[hash]
是针对webpack打包的所有文件,是整体的。[chunkhash]
是针对单个模块,是独立的- webpack 建议不要在开发环境使用
[chunkhash]
,因为会增加编译时间。我们可以将开发和生产环境的配置环境分开,在开发环境使用 [name].js 的文件名,而生产环境使用 [name].[chunkhash].js 文件名
上面的配置文件中,涉及到 entry
和 output
这两部分,下面就对这两部分做简要介绍。
入口文件(Entry)
入口文件的作用是告知 webpack 从哪里开始处理,打包哪些文件。入口文件可以有单个,也可以有多个。表现为以下几种形式:
单个形式输入,单个输出:
// webpack.config.js |
多个数组形式输入,合并单个输出:
// webpack.config.js |
多个对象形式输入,对应多个输出:
// webpack.config.js |
不过,我觉得对于工具库(比如 jquery),鉴于我们基本不会去修改它,可以考虑在页面里直接使用 <script src="src/jquery.js"></script>
的形式(只是页面多时,需要每个页面,手动引入),这样,也会大大提升编译速度。
以上三种形式对应的 output
写法相同,见下面说明。
出口文件(Output)
从某种程度来说,output
生成的文件数,主要取决于 entry
的结构:
entry
单个文件 ->output
单个文件entry
数组多个文件 ->output
合并成单个文件entry
对象多个文件 ->output
对应多个文件
无论输入是哪种形式,它总是包含 path
和 filename
两部分:
// webpack.config.js |
除了生成的文件数外,output
的另外一个重点,则是与 [hash]
、[chunkhash]
有关,这些内容在上面已着重介绍过。
这里面还有一个 publicPath
属性,你可能会混淆它与 path
的作用。不过没关系,后面会讲到 publicPath
的用法,当编译打包时,该属性特别有用。
三、结合 package.json
到目前为止,我们只使用到了 webpack -p
命令,可以很轻松的 hold 住。但当文件越来越多,项目越来越复杂,我们需要切换各种命令,以及在命令后面加不同参数。
此时,你会觉得记住各种命令,以及相关参数,是件头疼的事。有没有办法,可以将这些命令和参数全部列出来,形成类似键值对的映射关系,配置在某个文件中。这样,我们只需要对照映射表,运行对应命令即可。
当然有,你可以借助 package.json
文件。只需要通过里面的 scripts
属性:
在根目录的 package.json
中加入 start
命令:
// package.json |
此时,你在命令行面板运行 npm start
,你会发现编译打包的结果与 webpack -p
相同。
但如果你不想用 start
作为属性名,比如,下面使用的是 build
:
// package.json |
那么你得在命令行面板中,运行 npm run build
才能正常编译打包。因为对于 package.json
文件而言, npm start
是 npm run start
的缩写,所以,中间的 run 可以省略。
你可以添加更多的命令,如:
// package.json |
四、基础服务器 webpack-dev-server
很多时候,我们开发的项目都是前后端分离,即需要在服务器进行开发,调用相关的接口。并且,同时希望这个服务器能够自动检测到代码的变化,然后自动刷新浏览器。那么,此时你需要 webpack-dev-server
。
据官网介绍,webpack-dev-server
是一个小型的 Node.js Express 服务器,它通过 Sock.js 来连接整个服务。
安装它:
> npm install --save-dev webpack-dev-server |
启动服务
要启动 webpack-dev-server
服务器,需要先在 package.json
中设置启动命令:
// package.json |
在文章前面,页面里引用的js文件都是加 md5,为了避免启动服务器时,页面无法找到引用的js文件而出现报错的情况,我们先将添加 hash 处理的操作去掉,并重新编译下。
需要修改 index.html
、webpack.config.js
:
<!-- index.html --> |
// webpack.config.js |
运行 npm run build
,再次打包编译。此时,页面里引用的就是 main.js
了。
然后,我们再运行 npm run server
,启动devSever服务器,会看到 http://localhost:8080 ... webpack: Compiled successfully.
之类的提示。打开 http://localhost:8080/src/ 便可看到 index.html
页面。
文件监听、热更新
devServer 提供了很多配置选项,通过这些选项,我们可以使用 devserver 的不同功能。常见选项如下:
配置项 | 说明 | 默认值 |
---|---|---|
contentBase |
设置启动服务的目录 | 项目根目录 |
port |
服务器的端口号 | 8080 |
inline |
文件监听,设置 false 时,应用 iframe 模式 | true |
historyApiFallback |
页面找不到时(404),是否重定向到 index.html,设置 false 时,不重定向 | true |
其中最值得一提的,当属文件监听。要实现文件监听,必须在 webpack.config.js
中进行设置:
// webpack.config.js |
值得注意的是,webpack-dev-server
编译后资源(js、css等),在本地目录是无法看到的。因为为了提升编译效率,这些编译后的文件,都被暂存在内存中。你可以理解为文件每次修改后,devSever编译后的文件,都暂存在服务器对应目录下,只是这些文件对开发者不可见而已
因此,为了使得页面里正确引用到js文件,我们还得修改 index.html
中 main.js
的路径:
<!-- index.html --> |
npm run server
重启服务,然后,修改 main.js
中的内容,你会发现,页面也跟着刷新。有没有瞬间感觉页面开发效率提高了很多?
但又出现了一些新问题,当我们修改了 index.html
中的内容时,发现浏览器中的页面没跟着刷新。难道它这个监听刷新只针对页面引用的资源?随后,我们又尝试着在页面里引用一个 page.css
文件,修改 page.css
,结果页面还是无法自动刷新样式。
另外,还有前面提到的,当页面重新打包编译时,页面如何跟踪引用更新过md5的资源文件。
这些问题,就需要借助 插件(plugin) 来完成!
五、插件(plugin)
首先,要明白一点,插件是处理整个项目文件。
针对上一节无法监控html、css文件,以及页面内资源的正确引入问题,可以使用 html-webpack-plugin
插件。
html-webpack-plugin
安装它:
> npm install --save-dev html-webpack-plugin |
html-webpack-plugin
提供了很多配置选项,通过这些选项,我们可以使用 html-webpack-plugin
的不同功能。常见选项如下:
配置项 | 说明 |
---|---|
title |
生成的html文件的页面标题 |
filename |
生成的html文件名,默认是 index.html |
template |
要求打包的模板 |
inject |
向template或者templateContent中注入所有静态资源,有 true 、'head' 、'body' 、false 四个值。设置 true 或者 body 时,所有JavaScript资源都插入body底部,head 则插入head。 |
chunks |
插入页面模板的thunk文件,它的值是一个数组,表示该模板需要引入 entry 里的哪几个文件 。不配置的话,默认将 entry 里的所有 thunk 注入到模板中。 |
了解了这些配置选项后,我们重新对 webpack.config.js
进行调整:
// webpack.config.js |
因为 html-webpack-plugin
会跟踪页面引用的资源文件,所以 output
中的 filename 更新为加 md5 的文件。正因为如此,我们便可删除 index.html
中引用的 js文件()。<script src="main.js"></script>
此外,我们注意到,上面的代码中,还多了 plugins
这么一项。它的值是一个数组,我们可以往里面添加多个插件。
此时,再重新运行 npm run server
,便可看到,无论是修改 html文件、还是 js文件,页面都能自动刷新了。
而当运行 npm run build
,你会看到 dist
目录下会新生成 index.html
以及加了md5后缀的 main.js
文件。
注意:npm run server
后,即项目开发时,需要将 filename: __dirname + '/dist/index.html'
和 publicPath
(如果设置了)进行 注释。如果不注释,会导致服务运行的页面,无法找到相关资源。而打包时,则去除注释
下面介绍的插件,可以暂时跳过,先去了解 加载器(loader),之后才会用到以下插件。
extract-text-webpack-plugin
之前的 css 都是打包到 js 文件中,这样减少了请求数,当用户打开页面时,通过 <style type="text/css">...</style>
逐个插入到网页头部。
不过,当css多时,此种做法会导致js体积很大。
此时,你希望对js文件引入的css或者less文件单独外链,你可以安装 extract-text-webpack-plugin
:
> npm install --save-dev extract-text-webpack-plugin |
首先,你在入口js文件里引入 less:
// main.js |
然后,你需要在配置文件 webpack.config.js 里引入该插件模块,并且对其中的 module
和 plugins
进行相关设置:
// webpack.config.js |
这里指定了css生成目录和文件名为 css/style.[chunkhash].css
,再次运行 npm run server
或 npm run build
后,你会发现页面的样式被外链了。但不幸的是,样式里的图片不显示。因为css文件里背景图的路径为 url(res/images/big.png?57e396ba)
,而css文件在 res/css/
目录,但图片在 res/images/
,这显然引用不到,因为 res/css/
压根就没 res/images/
这么一个目录。
所以,我们需要在 loader
里 ExtractTextPlugin 部分新增一个 publicPath
属性,来覆盖原来 output
里设置的 publicPath
。
// webpack.config.js |
这样打包后的css文件,里面的背景图就都变成了 url(../images/big.png?57e396ba)
这样的路径了。
clean-webpack-plugin
随着我们一次一次的修改文件,又一次次的打包,我们会发现 dist 里的文件越来越多,因为这些文件还包括了之前打包过的。但对于单个项目而言,我们每次打完包,都是将dist里的文件直接上传到服务器。我们希望每次打完包后,dist 里只有本次打包的文件。那就需要在打包前,删除 dist 里的某些文件或清空整个dist文件夹或 dist 文件夹里的文件,打包完后,dist 只剩页面和当前引用的文件及相关资源。
要在打包前清空 dist 目录,可以通过以下两种方法:
配置npm
在配置文件 package.json 的 scripts 中,增加以下两项,其中 clean (npm run clean) 表示只清空目录,而 build (npm run build) 则表示清空目录,并编译打包文件:
// package.json |
注意,没有 dist,或者 dist下没有文件时,运行 npm run build 会报错,这种方式使用起来可能不那么灵活。
clean-webpack-plugin
如果不想配置使用 npm 这种方式,你还可以安装插件 clean-webpack-plugin:
> npm install --save-dev clean-webpack-plugin |
然后,你需要在配置文件 webpack.config.js
里引入该插件模块,并且对其中的 plugins
进行相关设置:
// webpack.config.js |
值得一提的是,CleanWebpackPlugin 除了可以移除指定的文件夹(文件)外,它还有第二个参数(可选),该参数可以指定移除的根目录,移除是否需要打印log等信息。
可以看到,npm 方式可谓是 简单粗暴,而 clean-webpack-plugin 则是功能丰富。
六、加载器(Loader)
与 plugin 不同,loader 主要是用于处理一类文件。比如说,将 css 通过 js 引入到页面中,或者将 es6、es7、jsx 转换为 es5,又或者将 sass、less 转换为 css,下面就介绍相关的 loader。
loader 执行的三种方式:命令行、单个文件require、配置文件
// 方式一 |
json-loader
该loader主要处理json文件。
首先需要说明的是,webpack2.0版本,已经自带 json-loader
,因此,你无需安装,也无需在 webpack.config.js
中配置,便可直接使用json文件了。
但对于1.0的版本,安装它:
> npm install --save-dev json-loader |
然后,在 webpack.config.js
中进行配置:
// webpack.config.js |
通过上面代码,可以看到。module.exports
中新增了 module
项,它有一个 loaders 属性,该属性值是一个数组,我们可以往里面添加更多的loader。
css-loader、style-loader
为了更接近更真实的项目开发,我们更改下项目目录:
src |
将所有的js文件都放在 res/js
这个目录下,同时更改 webpack.config.js
的路径。
// webpack.config.js |
这里 filename
的值前面加了一个 js 目录,目的是希望打包后的js文件都生成在js文件夹。而 path
的值则作为 css、js、images打包后的父级目录。
然后,我们再在 res/css
目录下新建个css文件 page.css
,在里面写点样式:
.css-box { |
接着,在 index.html
加入类名为 css-box 的div。
<!-- index.html --> |
在 main.js
里面引入这个css文件:
//main.js |
安装处理css的loader:
> npm install --save-dev css-loader style-loader |
css-loader
是让js(require)具备引入css文件(@import)的功能,而 style-loader
则是将计算后样式以 <style type="text/css">...</style>
的方式插入到页面head中。
同时安装多个包时,用空白隔开即可。
在 webpack.config.js
中,进行这两个loader设置:
// webpack.config.js |
运行 npm run server
,你会发现页面里类名为 css-box 的div,应用了相关样式。打开控制台,你会发现这些样式,以 <style type="text/css">...</style>
的形式被插入到页面的 <head>...</head>
中。它的原理是先将这些css拼接到 main.js
里的各个模块,当页面打开时,再动态插入到 head
中。
如果需要将css文件单独外链,可参见 plugin 部分的 extract-text-webpack-plugin
章节。
less-loader
处理完 css,我们接着处理 less。首先将 page.css
直接换成 page.less
,内容也作如下调整:
.css-box { |
然后,修改 main.js
里面这个css文件:
//main.js |
接着,安装相应的包:
npm install --save-dev less-loader less |
注意,安装 less-loader
的同时,还要安装 less
,否者会出现报错。
最后,配置 webpack.config.js
文件:
// webpack.config.js |
运行 npm run server
,在浏览器中,你将看到页面里类名为 css-box 的div,已经应用了相关样式。
而对于sass,应该也是使用相似的加载器。
babel-loader
如果你在 main.js
里写点 es6 的东西:
// main.js |
再运行 npm run build
去打包编译,你会发现此时命令行里报错了,出现 Unexpected token: name (amount)
之类的提示。因为,没有正确的加载器,webpack 默认是不能识别 es6 的语法。
你需要 babel-loader
来将 es6 转换为 es5,它包含了几个独立的包,一并安装它:
> npm install --save-dev babel-core babel-preset-es2015 babel-loader |
其中,babel-core
为 babel的核心模块,而 babel-preset-es2015
则是用于编译 es2016(es6)语法。
再在 webpack.config.js
里进行相关设置:
// webpack.config.js |
上面的代码中,exclude
表示不对node_modules这种依赖模块中的js做处理。再次运行 npm run build
,页面便可以正常打包编译了。
前端目前最主流、最热门的框架当属 react,babel-loader
除了可以编译 es6,还能对 react 进行编译。只需要安装 react
、react-dom
这两个被拆分的包,以及解析 react 语法的模块:
> npm install --save-dev react react-dom babel-preset-react |
然后设置配置文件 webpack.config.js
,由于同属 babel-loader
加载器。因此,只需要在 presets
加入 react
即可:
// webpack.config.js |
我们在 main.js
里加点 react 的代码,并且在 index.html
页面中加个 id 为 hello-react 的div容器:
// main.js |
<!-- index.html --> |
运行 npm run server
重启服务,刷新页面便可看到 id 为 hello-react 这个div里面的内容为 <div><h1>Hello, React</h1></div>
。
file-loader url-loader
除了文字,图片也是网页展示内容一个不可或缺的载体。其中,图片的表示形式又主要分为两种,即 html 中 <img src />
标签,以及css中的 background
背景图。
先来看看背景图部分,在 res/images
中新增 small.png
和 big.png
这两张图,并且分别在 index.html
和 page.less
中添加部分内容:
<!-- index.html --> |
/** page.less **/ |
npm run server
重启服务,发现编译失败了,提示内容没找到对应的加载器去处理文件类型。此时,你需要 file-loader
和 url-loader
。安装它:
> npm install --save-dev file-loader url-loader |
这两者都是用于处理文件的,主要是用于处理图片。url-loader 可以看成是 file-loader的过滤器,小图片(一般不大于8192字节)可以使用 url-loader ,然后将图片以 data uri 的形式嵌入到页面或样式中,这样减少请求数。 而对于大一点的文件,我们则使用 file-loader
。
在 webpack.config.js
里进行设置:
// webpack.config.js |
再次运行 npm run server
重启服务,此时,再看浏览器里的页面,这两个背景图都能正常显示。打开控制台中的样式面板,你会发现小图背景被转换为 data uri 格式,而大图背景的图片名则是一个32位md5加密的字符串。
运行 npm run build
,打包编译后,在 dist
目录打开 index.html
页面,此时发现大图无法正常显示。再仔细查看 dist 目录,发现大图被打包到了与 js 同一目录。所以,样式里引用不到这个图,这显然不是我们想要的结果。
默认情况下,当不对图片的输出路径以及名称进行设置时,图片会直接打包到引用 less 模块(即js文件)所在的目录,并且文件名为32位 md5 的hash值。
而我们想要的结果是,图片输出路径与 src
一致,为了方便识别,图片的名称也最好是一致,为了防止缓存,还需要在图片名后加md5。
要想以指定名称、指定图片的生成目录来打包图片,只需要对前面的 module 中的 url-loader 进行修改:
// webpack.config.js |
其中,name=images/[name].[ext]
遵循 name=[path]/[name].[ext]
规则,[path]
表示打包后图片的目录,[name]
为文件名,[ext]
为扩展名,[hash:8]
为md5处理过的 hash 值,默认为 32位,这里设置了 8位。
再次运行 npm run build
,打开打包后的 index.html
页面,结果发现 <div class="big"></div>
这个元素的背景还是无法显示。
控制台中显示它背景url的路径为 url(images/big.png?57e396ba)
,而打包后图片的目录为 dist/res/images
,并且样式是内嵌在 index.html
里的,这显然是引用不到图片。要让图片显示,我们必须得将 url(images/big.png?57e396ba)
转变成 url(res/images/big.png?57e396ba)
。
怎么处理?还记得我们前面提到的 publicPath
吗?这时候就该它大显身手了。
在 output
中加入 publicPath
属性:
// webpack.config.js |
运行 npm run build
,打开打包后的 index.html
页面,<div class="big"></div>
这个元素的背景正常显示了。
可以发现,在没设置 publicPath
属性之前,js文件被打包到 output
中 path
属性所指定的目录,而图片被打包到了 url-loader
所指定的目录
但当设置了 publicPath
这个属性后,打包编译完后,js、images、css 这些静态资源文件夹,都会生成在 publicPath
指定的目录下
比如,上面的例子,publicPath
的值为 res/
。其中 main.js
指定的输出路径为 'js/[name].[chunkhash].js'
,那么,打包后页面引用的路径就变成 res/js/main.js
。而图片指定的输出路径为 images/[name].[ext]?[hash:8]
,那么,打包后css引用的路径就变成 res/images/big.png
。
而如果我们要将打包后的项目发布到线上或引用cdn资源,只需要将 publicPath
的值改成 http://www.xx.com/project/res
即可。
当然,设置 publicPath
也不是万能的。假设你不想样式内嵌到网页中,而是希望以外链文件的方式引入页面,那也没问题,可通过 插件-ExtractTextPlugin,但由于css是外链的,图片路径又会出现问题,解决方案见 插件-ExtractTextPlugin 章节。
html-loader
说完css背景图,我们再来看看 html 页面里,类似 <img src="res/images/hook.png" alt="">
这种图片的引入方式。
在 index.html
中加入以下内容:
<!-- index.html --> |
打包后,发现 big.png
这张大图会根据 url-loader 里定义的规则,打包到了对应目录下,且是 md5、8位hash值名称的图片。
而 small.png
这张小图(小于8192字节)在页面里不显示。这时候,我们需要结合 html-loader
来进行打包:
> npm install --save-dev html-loader |
然后,在module里进行设置:
// webpack.config.js |
再次运行 npm run build
进行打包,刷新页面,你会发现 small.png
这张图被转换为 data uri 格式,可以正常显示了。
而对于 jsx 里引用的图片,使用它们指定的语法即可,它可以是这样:
// main.js |
也可以是这样:
// main.js |
打包规则也相同,大图仍然是 图片名+md5,小图则是 data uri。
六、配置优化
webpack最难的地方在哪?当然是配置文件,尤其是涉及到多个环境的配置。
一般来说,对于一个项目而言,都有两套对应的环境,即 开发(dev)环境、生产(prod)环境。有时为了项目安全、方便测试,还会配置一套与生产环境相似的 集成(inter)环境。
通过前面的内容,我们知道,开发环境和生产环境的配置有很多相同的地方,但也存在一切差异。
比如,devServer
、HMR
这些设置只是针对开发环境,而生产环境则需要设置 output
里 publicPath
属性,开发环境却无需配置。
所以,最简单的方式是建立两个配置文件 webpack.config.dev.js
和 webpack.config.prod.js
,独立维护。
然后再更改 package.json
文件:
// package.json |
通过不同的命令,设置不同的配置文件。