geekwen.com

使用 webpack 打包构建的一些经验

在最早接触 webpack 的时候,仅仅是用它来把模块化的 js 打包到一个文件中去。那时候只有 js 参与了打包构建的过程,css 和图片的处理方式都很”原始”,都是用各自的第三方工具进行了压缩。前段时间为了加速页面资源加载,对 webpack 和前端工程化的问题进行了更进一步的研究。优化页面静态资源的加载关键的点就是:请求过的所有资源都缓存起来,下次再次请求时如果文件内容没有变动则不与服务器沟通直接使用缓存,否则请求新文件。所以就引申出这么几个问题:

  1. 怎么才能最直观的体现出文件的改动?
  2. 不与服务器沟通浏览器怎么知道文件的改动?(或者说怎么避免浏览器请求没有变动的文件?)

答案就是:

  1. 文件以根据其内容生成唯一的、固定的 hash 值来作为文件名。这样的话,文件内容没有改变时文件名是固定的,一旦内容改变文件名也随之而变。
  2. 使用强缓存。同一个文件只有当缓存过期才去服务器请求新的。

关于浏览器缓存,下面有几篇文章介绍得很详细:

  1. Web缓存相关知识整理–2016-06-02
  2. 浏览器缓存知识小结及应用–2016-01-15 12:59
  3. 缓存策略–2015-08-09 14:56

那文件使用 hash 来命名,每次模块修改了以后文件名都不一样,页面如何准确的引用呢?这必须要使用到 html-webpack-plugin 组件。它可以用指定的模版来生成对应的 html 或者模版文件,并把 js 和 css 的引用插入进去。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
plugins: [
...
new HtmlWebpackPlugin({
filename: 'tpl/pages/index.ejs',
template: '!!raw-loader!views/ejs/index/index.ejs', // 用 raw-loader 是为了不让将 ejs 中的语法变量编译出来
chunks: ['manifest', 'index'],
minify: {
removeComments: true,
collapseWhitespace: true,
caseSensitive: true,
},
chunksSortMode: 'manual'
}),
...
]

注意:配置文件中 template 用的是 raw-loader 而不是 ejs-loader。如果 ejs 模板中是有变量需要 node 服务运行时才能确定,或者模板使用了 include 方法的话,必须要用 raw-loader 把模板作为普通文本加载。否则用 ejs-loader 编译时会尝试把变量编译成确定的值,而此时变量又无法确定的话,编译会报错。其他参数的含义请查看文档。

对于 css 和图片,处理其实比较简单。在 js 入口文件把 css 文件 require 进来,然后配置好相应的 css loader 以及图片文件 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
module: {
rules: [
...
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'postcss-loader', options: {sourceMap: true}},
{loader: 'resolve-url-loader'},
{loader: 'sass-loader', options: {sourceMap: true}}
],
publicPath: '../'
})
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'images/dist/[md5:hash:base64].[ext]'
}
},
{
loader: 'image-webpack-loader' // 压缩图片
}
]
}
...
]
}

我使用了 scss 和 postcss,所以需要 sass-loaderpostcss-loader。这里面 loader 的顺序不能乱。ExtractTextWebpackPlugin 的功能是将引入的 scss 代码(经 sass-loader 等编译后)分离成单独的 css 文件。其中 resolve-url-loader 是用来处理 scss 中图片引用路径的问题。

在 webpack 处理 scss(css) 时,会自动处理图片,因此需要使用 url-loader 来加载图片文件。通过配置 limit 来限制图片大小:图片大小小于大小限制的将转成 base64 字符串写进 css 中,大于大小限制的使用 name 来写成对应的文件。另外图片还可以用 image-webpack-loader 来对图片文件进行压缩。

经过近段时间的版本迭代,webpack 的文档比起最初 1.x 的版本还是好了很多。反复咀嚼文档中的细枝末节,就能够把构建中的大小事宜理清楚。

关于前端工程化的问题,更详细的内容请看 前端工程与性能优化–2014-05-02。虽然是14年的文章,but 写得超详细。仔细读完后能够对前端工程化有清晰的认识。