Plugins

30 阅读4分钟

上一篇的loader其主要功能还是配置webpack如何加载文件,而webpack的plugins可以做很多事情,比如定义全局变量、压缩文件、打包的优化等等。

压缩bundle文件

更小的 JS 文件可以使用户加载网页更快,所以压缩操作是很有必要的。 可以看看上一章结束时我们打包的 bundle.js 的大小,目前是 20kb, 我们使用 TerserPlugin 来对 bundle进行压缩。

const TerserPlugin = require('terser-webpack-plugin');
...

    plugins: [
       new TerserPlugin()
    ]

(plugins属性在配置文件中是和module mode等属性平级的) 配置好之后直接打包,观察新的bundle.js仅仅有8kb, 这说明压缩配置生效了(terser-webpack-pluginwebpack内置的,所以不用另外去下载)

拆分CSS文件

上一篇我们暂时使用loader处理csssass文件,会把样式直接打包进bundle.js中。但假设是较大的工程,这样会使最后打包的bundle非常大,影响浏览器加载的速度。要充分利用浏览器可以并行加载网络资源的特点,正常情况下,是要把样式文件单独拆包出去,这里使用的pluginmini-css-extract-plugin

注意mini-css-extract-plugin并非是webpack5内置的,所以需要下载npm

npm install mini-css-extract-plugin --save-dev

配置文件需要加载,并在plugins中初始化

...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
plugins: [
    new TerserPlugin(),
    new MiniCssExtractPlugin({
        filename: 'styles.css',
    })
]

其次仍要指明loader的, 将styles-loader改写成MiniCssExtractPlugin.loaderstyles-loader可以将样式写入JS-bundle中)

{
    test: /\.css$/,
    use: [
        MiniCssExtractPlugin.loader, 'css-loader'
    ]
},
{
    test: /\.scss$/,
    use: [
        MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'
    ]
},

此时打包会发现dist目录中出现了css文件,别忘了在模版html中引入才能生效

<link rel="stylesheet" href="./dist/styles.css" />

浏览器缓存

缓存策略可以使浏览器已经加载过的文件不再重复加载,这里不展开。但也带来一个问题,如果同名文件内容更改了,浏览器却命中了缓存这会使用户无法看到新上线的内容。一般情况下采用文件生成md5 hash的方式来区别是否有内容更新。webpack内置了这个功能,只需在文件名加入关键字[contenthash]

output: {
    filename: 'bundle.[contenthash].js',
    path: path.resolve(__dirname, './dist') ,
    publicPath: 'dist/'
},
...
plugins: [
    new TerserPlugin(),
    new MiniCssExtractPlugin({
        filename: 'styles.[contenthash].css',
    })
]

打包后的结果如下

image.png

webpack帮我们生成了文件的md5 hash, 在文件内容不更新的情况下,这个值不会发生变化。

自动清理dist文件

上面的一节虽然生成了带有hash的文件,但是每当新文件生成,老文件还在,webapck是否能帮我们维护dist文件夹的整洁呢?官网文档表明可以在 output 属性中配置 clean: true,来清理dist文件夹。当然这是OK的,也可以使用 clean-webpack-plugin 来做这件事儿,先加载这个Npm

npm install clean-webpack-plugin --save-dev

再来修改配置文件:

...
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
...
plugins: [
    new TerserPlugin(),
    new MiniCssExtractPlugin({
        filename: 'styles.[contenthash].css',
    }),
    new CleanWebpackPlugin(),
]

配置完成后再打包,会发现之前旧的文件都不见了。 为什么官方已经有内置的方法来清理文件,还有费事引入其他的工具?因为 CleanWebpackPlugin 支持对打包后的其他内容进行删除,甚至是dist文件夹的上层文件夹的内容,正常情况下用官方的clean属性就够了,如果有一些特殊的需求,可以使用 CleanWebpackPlugin.

自动生成HTML

如果用hash的方式打包,还会有一个问题就是我们的html模版文件引入js css 资源的时候失败了,毕竟我们的模板调用资源的路径没有文件的hash信息,这时候可以用 HtmlWebpackPlugin,可以在打包时候生成一个html文件,里面的资源引用路径也是有hash值的文件名。这个 plugin 也不是内置的,需要手动加载

npm install html-webpack-plugin  --save-dev 

配置文件初始化plugin

...
const HtmlWebpackPlugin = require('html-webpack-plugin');
...
 plugins: [
    new TerserPlugin(),
    new MiniCssExtractPlugin({
        filename: 'styles.[contenthash].css',
    }),
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin(),
]

值得注意的是需要改一下PublicPath,因为生成的html文件和其它bundle现在是同级目录下

使用Html模版

HtmlWebpackPlugin是可以通过配置拓展的

new HtmlWebpackPlugin({
    title: 'Hello world',
    filename: 'subfolder/custom_filename.html',
    meta: {
        description: 'Some description'
    }
})

filename还可以代路径来指定目录层级,以及通过meta属性指定一些htmlmeta属性等等,更多的配置可以看HtmlWebpackPlugin的 github上面的介绍

模版引擎可以使用handlebars

npm install --save handlebars 
npm install --save-dev handlebars-loader

修改htmlwebpackplugin的配置

new HtmlWebpackPlugin({
    title: 'Hello world',
    filename: 'index.html',
    template: 'src/index.hbs',
    description: 'Some description',
}),

因为需要引入.hbs的文件,所以要增加相应的loader

{
    test: /\.hbs$/,
    use: [
        'handlebars-loader'
    ]
}

src/index.hbs的内容

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{htmlWebpackPlugin.options.title}}</title>
    <meta name="description" content="{{htmlWebpackPlugin.options.description}}">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
  </body>
</html>

注意里面有{{}}的语法,可以将配置文件中的一些属性打包进生成的html文件的指定位置

配置完成后打包即可得到预期的html文件内容。