插件机制介绍

概述

插件机制是webpack另外一个核心特性,他的目的是为了增强webpack在项目自动化方面的能力,那我们都知道,loader就是负责实现我们项目中各种各样资源模块的加载, 从而去实现整体项目的打包,而plugin则是用来解决项目中除了资源加载以外,其他的一些自动化的工作。

例如plugin可以帮我们去实现自动在打包之前清除dist目录,也就是我们上一次打包的结果。又或是他可以用来帮我们去copy那些不需要参与打包的资源文件到输出目录,又或是他可以用来帮我们压缩我们打包结果输出的代码。

总之,有了plugin呢webpack几乎无所不能的实现了前端工程化当中绝大多数经常用到的工作,这也是很多初学者会有webpack就是前端工程化的这种理解的原因。

接下来我们一起来学习下webpack插件机制以及这个过程中经常遇到的插件,最后我们再来开发一个自己的插件去理解他的工作原理。

常见的插件介绍

介绍几个最常见的插件,然后通过这个工程去了解如何使用插件。

  1. clean-webpack-plugin

自动清除输出目录的插件。

通过之前的演示你可能已经发现,webpack每次打包的结果都是覆盖到dist目录,而在打包之前,dist中可能已经存在一些之前的遗留文件,那我们再次打包,他可能只能覆盖那些同名的文件,对于其他那些已经移除的资源文件就会一直积累在里面,非常不合理。

更为合理的做法就是每次打包之前,自动去清理dist目录,那这样的话dist中就只会保留那些我们需要的文件。

clean-webpack-plugin就很好的实现了这样一个需求,他是一个第三方的插件,我们需要先安装他

yarn add clean-webpack-plugin --dev

安装过后我们回到webpack配置文件,然后去导入这个插件。这个webpack模块导出了一个叫做clean-webpack-plugin的成员,我们把它解构出来,然后回到配置对象当中。

那这里使用插件我们需要为配置对象添加一个plugins属性,那这个属性就是专门用来去配置插件的地方,那他是一个数组,我们去添加一个数组,就是在这个数组中去添加一个元素。

绝大多数插件模块导出的都是一个类型,我们这里的clean-webpack-plugin也不例外,所以我们使用他就是通过这个类型去创建一个实例,然后将这个实例放入到plugins这个数组当中。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin()
    ]
}

完成以后我们再来尝试一下,回到命令行打包,此时发现,之前那些打包的结果就不会存在了,dist中就都是我们本次打包的结果,非常的干净。

  1. html-webpack-plugin

还有一个非常常见的需求就是自动去生成使用打包结果的html,在这之前我们的html都是通过硬编码的方式,单独去存放在项目的跟目录下的,那这种方式有两个问题。

第一就是我们在项目发布时,我们需要用时去发布跟目录下的html文件和dist目录下所有的打包结果,这样的话相对麻烦一些。而且我们上线过后还需要去确保html代码当中路径,引用都是正确的。

第二个问题就是,如果说我们输出的目录或者是输出的文件名也就是我们打包结果的配置发生了变化,那html代码当中script标签所引用的那个路径也就需要我们手动的去修改,这是硬编码的方式存在的两个问题。

解决这两个问题最好的办法就是通过webpack自动去生成我们的html文件,也就是让html也自动参与到html构建的过程中去,那在构建过程中webpack知道生成了多少个bundle,会自动将这些打包的bundle添加到我们的页面当中。这样的话一来我们的html他也输出到了dist目录,上线时我们只需要把dist目录发布出去就可以了,二来我们html当中对于bundle的引用他是动态的注入进来的,不需要我们手动的去硬编码。所以他可以确保路径的引用是正常的。

具体的实现方式我们需要去借助一个叫做html-webpack-plugin的插件去实现,这个插件同样也是一个第三方模块,这里我们同样也需要单独去安装这个模块。

yarn html-webpack-plugin --dev

安装完成过后我们回到配置文件当中,然后载入这个模块。这里不同于clean-webpacl-plugin那html-webpack-plugin默认导出的就是一个插件的类型,我们不需要解构他内部的成员。

有了这个类型过后我们就可以回到配置文件的plugins属性当中,然后去添加一个这个类型的实例对象。这样的话就完成了我们这个插件的配置。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin()
    ]
}

最后我们回到命令行终端,再次运行打包命令。此时我们的打包过程中就会自动生成一个index.html的一个文件,输出到dist目录。我们找到这个文件。

这个文件中的内容就是一段使用了我们bundle.js的一个空白的html,不过呢,这里的路径还是有一点问题,正确的路径应该是当前目录下的bundle.js而我们这里确生成了dist/bundle.js。

这是因为之前我们去尝试其他特性的时候我们去把output属性当中的publicPath设置成了dist,那现在呢我们的html是自动生成到了dist目录,所以我们就不再需要这样一个配置了,我们回到配置文件中去删除这样一个配置。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        // publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin()
    ]
}

删除完成过后我们再次重新打包。打包完成过后我们的index.html当中对于bundle引用的路径已经正常了。至此我们就不再去需要跟目录下写死的html文件了,以后我们html文件都是通过自动取生成出来的。

但是这里仍然存在一些需要改进的地方,首先就是我们对于默认生成的html的标题是必须要修改的,另外我们很多时候还需要自定义页面中的一些原数据标签和基础的DOM结构,对于简单的自定义的话我们可以通过修改html-webpack-plugin这个插件一些属性来去实现,我们回到webpack的配置文件当中。

这里我们给html-webpack-plugin这个构造函数去传入一个对象参数,用于去指定我们的配置选项,那title属性就是用来设置我们html的标题。我们这里设置为webpack-plugin-simple。

meta属性可以以对象的属性设置页面中的一些原数据标签,例如我们尝试为页面添加一个viewport的设置。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        // publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Plugin Sample',
            meta: {
                viewport: 'width=device-width'
            }
        })
    ]
}

完成以后我们回到命令行重新打包,我们看下生成的html文件,此时这里的title和mate就根据我们配置文件当中的配置去生成。

如果我们需要对html文件进行大量的自定义的话,最好的做法就是在原代码当中添加一个用于去生成html文件的一个模板,然后让这个html-webpack-plugin的插件根据我们这个模板去生成页面。

我们在src目录下新建一个index.html的html模板,然后我们可以根据我们的需要在这个文件中添加一些相应的元素,对于模板当中我们希望动态输出的一些内容我们可以使用loadsh模板语法的方式去输出。这里我们可以通过htmlWebpackPlugin.options这个属性去访问到我们这个插件的配置数据, 那配置数据当中的title我们就可以直接输出出来。

当然htmlWebpackPlugin这个变量实际上是他内部提供的一个变量,也可以通过另外的属性去添加一些自定义的变量。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
    <script src="dist/"></script>
</body>
</html>

有了这个模板文件过后呢,我们回到配置文件当中,我们通过template属性去指定我们所使用的模板为src/index.html文件

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        // publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Plugin Sample',
            meta: {
                viewport: 'width=device-width'
            },
            template: './src/index.html'
        })
    ]
}

再次打包,我们看下所生成的html文件,此时我们看到的html内容就是根据我们刚刚的模板去动态生成的了,以上就是我们自定义输出html内容的一些方式。

多个页面文件也是一个非常常见的需求,除非说我们的应用是一个单一页面应用程序,否则的话我们就一定需要多个html文件。

如果我们需要输出多个html文件其实也非常简单,我们回到配置文件当中。

这里我们刚刚通过html-webpack-plugin创建的对象,他是用于去生成index.html这个文件的。

那我们完全可以再去通过这个类型创建一个新的实例对象,用于去创建额外的html文件。

例如我们这里再去添加一个新的实例,然后用于去创建一个叫做about.html的页面文件,我们可以通过filename去指定输出的文件名,这个属性的默认值是index.html, 我们这里需要设置为about.html。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        // publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Plugin Sample',
            meta: {
                viewport: 'width=device-width'
            },
            template: './src/index.html'
        }),
        new HtmlWebpackPlugin({
            filename: 'about.html'
        })
    ]
}

重新打包, 此时dist目录下就同时生成了index.html和about.html两个页面文件。

根据这样一个尝试我们就知道,如果说我们需要去创建多个页面,那我们就可以在插件列表当中去加入多个htmlWebpackPlugin实例的对象,每个对象呢就是用来去负责生成一个页面文件的。

  1. copy-webpack-plugin

项目中一般会存在许多文件,不仅在开发环境中会使用到,他们最终也会发布到线上,例如我们网站的favicon.ico, 一般我会把这一类的文件统一放在项目根目录下的public目录当中。我们希望webpack在打包时可以一并将他们复制到输出目录。

对于这种需求我们可以借助于copy-webpack-plugin去实现。同样我们需要先安装这个插件。

准备public/favicon.icon文件。

yarn add copy-webpack-plugin --dev

安装完成过后我们回到配置文件当中,再去导入这个插件的类型。最后我们同样在这个plugins属性当中去添加这个类型的实例。

那这个类型的构造函数他要求我们传入一个数组,用于去指定我们需要copy的文件路径,那他可以是一个通配符,也可以是一个目录或者是文件的相对路径。

我们这里传入的是一个public/**目录。表示在打包时会将public目录下所有的文件全部拷贝到输出目录。

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        // publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'file-loader'
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Plugin Sample',
            meta: {
                viewport: 'width=device-width'
            },
            template: './src/index.html'
        }),
        new HtmlWebpackPlugin({
            filename: 'about.html'
        }),
        new CopyWebpackPlugin([
            'public/**'
        ])
    ]
}

完成以后我们重新打包,打包完成过后呢我们public目录下所有的文件就会同时copy到输出目录了。

总结

至此我们就了解了几个非常常用的插件,那这些插件呢一般都适用于任何类型的项目。不管说你的项目中有没有使用框架或者是使用了哪一个框架。那他们都基本上会用到。

所以之后最好能仔细去过一遍这些插件的官方说明,然后去看看他们还可以有哪些特别的用法,做到心中有数。

除此之外社区当中还提供了成百上千的插件,你并不需要全部认识,在你有一些特殊的需求时,再去提炼你需求当中的一些关键词然后去github上去搜索他们。

例如我们想要去压缩输出的图片,那我会去搜索imagemin webpack plugin。

虽然每个插件的作用不尽相同,但是他们在用法上几乎都是类似的。

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):