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 |
通过不同的命令,设置不同的配置文件。