webpack二刷之三、Plugin 插件机制

本文深入探讨Webpack插件机制,介绍其如何增强项目自动化能力,包括常见插件如clean、html及copy-webpack-plugin的使用,以及插件机制的底层原理与自定义插件开发流程。

Plugin 插件机制

webpack 插件机制

目的:增强webpack项目自动化能力。

Loader 专注实现 资源模块的加载,从而实现整体项目的打包。

Plugin 解决项目中 除了资源加载以外 的自动化工作。

例如

  1. 在打包之前,自动清除dist目录
  2. 拷贝不需要参与打包的资源文件到输出目录
  3. 压缩打包结果输出的代码

webpack + Plugin 实现了大多前端 工程化 的工作,也是很多开发者理解为 webpack就是前端工程化 的原因。

webpack 常用插件

  • clean-webpackl-plugin
  • html-webpack-plugin
  • copy-webpack-plugin

绝大多数插件导出的都是一个模块类(class),所以使用时就要创建这个插件的示例,并配置到plugins属性中。

clean-webpack-plugin 自动清理输出目录
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
	// ...
	plugins: [
		new CleanWebpackPlugin()
	]
}
html-webpack-plugin 自动生成html

目前位置示例都是以硬编码的方式,单独存放在项目根目录下,这个方式有两个问题:

  1. 项目发布时需要同时发布根目录下的 html文件 和 dist目录下所有的打包结果,并且上线后,需要人工确认html中路径引用都是正确的,相对麻烦。
  2. 项目输出(output)的目录或文件名,也就是打包结果的配置,发生了变化,还需要手动去修改html中script标签的src

解决办法就是通过webpack输出html文件,也就是让html也参与到webpack的构建过程。

构建过程中

  1. webpack知道生成了多少bundle,它会自动将这些打包文件添加到html文件中。
  2. webpack还把html文件输出到dist目录,这样上线时直接发布dist目录即可。
  3. bundle文件是webpack动态的注入到html文件中,不需要手动的硬编码,它确保路径的引用是正常的。

html-webpack-plugin 默认自动生成一个空白的index.html文件到配置的输出目录,并将打包文件(bundle)动态注入到html中。

html-webpack-plugin 配置

通过修改插件的配置属性,对自动生成的html文件做一些简单改进,例如:

  1. title:配置标题
  2. meta:自定义元数据标签

通过配置template指定一个html模板,可以配置更多内容以及自定义基础的dom结构。

html模板默认使用lodash语法。

同时输出多个页面文件

html-webpack-plugin 默认生成一个index.html文件。

同样可以通过创建多个实例,生成多个html文件。

通过filename指定输出的文件名。

copy-webpack-plugin 复制静态文件

对于项目中不需要参与构建的静态文件,也需要发布到线上,它们一般统一放在项目的public目录中。

可以通过 copy-webpack-plugin 插件将它们拷贝到输出目录。

它接收一个文件相对路径或目录组成的数组,路径可以使用通配符。

// 拷贝文件 v5.x用法
// new CopyWebpackPlugin([
	// 'public/**',
  // 'public'
// ])

// 拷贝文件 v6.x使用patterns 配置
new CopyWebpackPlugin({
  patterns: [
    // 'public/**'
    'public'
  ]
})

插件机制的工作原理

相比于Loader,Plugin拥有更宽的能力范围。

webpack要求插件(在plugins中使用时)必须是一个函数或者是一个包含apply方法的对象。

官方:插件是由[具有apply方法的prototype对象]所实例化出来的。

webpack在启动时,会自动调用插件的apply方法。

apply方法接收一个compiler对象作为参数。

compiler对象是webpack工作过程中最核心的对象。

它包含当前构建的所有的配置信息。

可以用它来注册钩子函数。


一般都会把一个插件定义为一个类(class),然后在其中定义一个apply方法。

使用插件时,通过这个class构建一个实例。

钩子机制

Plugin通过钩子机制实现。

钩子机制类似web中的事件。

为了便于插件的扩展,webpack几乎给每一个环节都埋下了一个钩子。

开发插件时就可以,向这些不同的节点,挂载不同的任务。

可以去webpack官网查看它预先定义好的钩子(API-PLUGINS - Compiler Hooks)

开发一个插件

自定义一个用于清除bundle.js中/*[*+]*/注释的插件。

确认插件执行时机(即挂载到哪个钩子上)

  • emit`钩子:确认bundle.js内容,在生成到output目录之前。

通过compiler.hooks访问到具体的钩子。

通过tap方法注册一个钩子函数,它接收两个参数:

  1. 参数1:插件的名称
  2. 参数2:需要挂载到这个钩子上的函数,它接收一个compilation对象作为参数
    1. compilation可以理解为此次打包过程的上下文
    2. 此次所有打包过程产生的结果都会放到compilation对象中
    3. compilation.assets访问所有编译的资源文件,每个元素都是一个包含资源文件信息的对象
      1. key是资源文件的名称
      2. value包含一个source()方法
        1. 调用它可以获取编译文件的内容
        2. 重定义这个方法返回的值,实现修改编译文件的内容
          1. 注:webpack还要求必须返回一个size()方法,用于返回编译文件的大小
class MyPlugin {
  apply(compiler) {
    compiler.hooks.emit.tap('MyPlugin', compilation => {
      // 打印资源文件的名称
      // console.log(name)

      // 打印资源文件的内容,通过资源文件的值的source方法获取
      // onsole.log(compilation.assets[name].source())
      
      // 判断是否是js文件
        if (name.endsWith('.js')) {
          // 获取内容
          const contents = compilation.assets[name].source()
          // 清除/*[*+]*/注释
          const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
          // 覆盖编译内容
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => withoutComments.length, // 必要属性
          }
        }
    }
  }
}
总结

插件通过在生命周期的钩子中挂载函数实现扩展。

在插件开发中最重要的两个资源就是 compilercompilation 对象。理解它们的角色是扩展 webpack 引擎重要的第一步。

  • compiler对象代表了完整的webpack环境配置。
    • 这个对象在webpack启动时一次性建立,并配置好所有可操作的设置。
    • 当在webpack环境中应用一个插件时,插件将受到此compiler对象的引用。
    • 可以使用它来访问webpack的主环境
  • compilation对象代表了一次资源版本构建。
    • 当运行webpack开发环境中间件时,每检测到一个文件变化,就会创建一个新的compilation,从而生成一组新的编译资源。
    • 一个compilation对象表现了当前模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。
    • compilation也提供了很多关键时机的回调,供插件做自定义处理时选择使用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值