美文网首页前端工程化
【webpack进阶系列】plugin的原理探究

【webpack进阶系列】plugin的原理探究

作者: 0月 | 来源:发表于2019-10-01 21:12 被阅读0次

前言

plugin 顾名思义就是插件的意思,它在webpack的运行过程中起到了非常重要的作用。其实webpack 80%的代码都是基于plugin实现的,可以说它是一个汇集各种plugin的大集合,里面有各种各样的plugin分别实现不同的功能,plugin就是webpack的灵魂!

plugin的作用

plugin是运行在webpak打包过程中的某段逻辑,它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作。

常用的plugin:
html-webpack-plugin,这是一个生成html的插件,每次打包都会调用它来接收生成的静态资源,如下面的例子

  • 新建一个项目名为plugin的目录,cd进入这个目录,执行初始化命令 npm init -y, 按回车键确认
  • 在plugin目录建一个src目录,并且在src目录下建一个index.js作为入口文件,随便在index.js中写点内容
console.log('index.js')
  • 安装插件
npm install webpack webpack-cli html-webpack-plugin clean-webpack-plugin -D
  • 在plugin根目录下建一个webpack.config.js, 配置webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    plugins: [
        new HtmlWebpackPlugin(),
        new CleanWebpackPlugin(), // 清除dist目录
    ]
}
  • 在package.json中配置打包命令"build": "webpack"
{
  "name": "plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.41.0",
    "webpack-cli": "^3.3.9"
  }
}

  • 执行打包命令
npm run build

详细如下图
打包就会生成一个dist目录,下面有main.js与index.html


1.PNG

在这过程中我用到了两个plugin : html-webpack-plugin 与 clean-webpack-plugin,他们的作用相信大家都知道了。

那么它到底是怎么起到作用的呢?

前面我介绍过plugin的作用:
它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作。

接下来,我们就根据功能来模仿html-webpack-plugin来写一个MyHtmlWebpackPlugin体会一下plugin是怎么一回事。

首先,我们在写之前要清楚plugin是怎么写的,有什么要求,去官方文档https://webpack.js.org/api/plugins/https://webpack.js.org/contribute/writing-a-plugin/
查阅后原来plugin就是一个类,

它的主要的步骤如下:

  • 编写一个JavaScript class
  • 在class里面写一个apply方法,这个方法接收一个参数compiler,表示这次打包的上下文。
  • 指定挂载的webpack事件钩子。
  • 处理webpack内部实例的特定数据。
  • 功能完成后调用webpack提供的回调。

编写插件之前要理解compiler和compilation两个对象,以及webpack生命周期的各个阶段和钩子。

然后我们开始写,还是在plugin这个项目里面写,在src目录下新建一个plugins的目录,在plugins目录下新建一个MyHtmlWebpackPlugin.js

写之前我们理清一下思路:
1、写一个class 里面有一个apply方法,接收一个compiler参数,开始写

class MyHtmlWebpackPlugin {
    apply(compiler) {}
}

2、然后指定挂载的webpack事件钩子,在里面做我们要做的事情,我们要做什么事情?
我先查一下这个https://webpack.js.org/api/compiler-hooks/文档看一下有哪些钩子可以用,然后我们的插件的目的很明确,就是拿到打包后的资源,插入到html中,并且生成一个html到dist目录下。

3、所以接下来就是在对应的钩子干活了,在apply方法里面写这些逻辑。

class MyHtmlWebpackPlugin {
    apply(compiler) {
        // 在 emit 这个异步钩子里面拿到生成好的静态资源,第一个参数必须和我们的插件同名'MyHtmlWebpackPlugin'
        compiler.hooks.emit.tapAsync('MyHtmlWebpackPlugin', (compilation, callback) => {

// 给一个默认的html
            const html = `
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyIndex</title>
  </head>
  <body>
  </body>
</html>
`

            // 提取js文件
            const scriptFile = '<script type="text/javascript" src="{}"></script>'
            const scriptArr = []
            for (let filename in compilation.assets) {
                if (/\.js$/.test(filename)) {
                    const file = scriptFile.replace('{}', filename)
                    scriptArr.push(file)
                }
            }
            // 把提取到js文件插入html里面去,返回一个新的html
            const newHtml = html.replace('</body>', scriptArr.join('\n') + '</body>')

            // 最后在compilation.assets下面挂载一个新的命名为MyIndex.html输出文件,里面有它的内容和内容的长度
            compilation.assets['MyIndex.html'] = {
                source: function () {
                    return newHtml;
                },
                size: function () {
                    return newHtml.length;
                }
            };

            // callback这个回调方法必须要执行
            callback();
        });

    }
}

// 导出这个plugin
module.exports = MyHtmlWebpackPlugin


4、 来试一下我们写的插件,把webpack.config.js中的html-webpack-plugin注释,换成我们写的MyHtmlWebpackPlugin

const path = require('path');
// const HtmlWebpackPlugin = require('html-webpack-plugin')
const MyHtmlWebpackPlugin = require('./src/plugins/MyHtmlWebpackPlugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    plugins: [
        // new HtmlWebpackPlugin(),
        new MyHtmlWebpackPlugin(),
        new CleanWebpackPlugin(), // 清除dist目录
    ]
}

5、npm run build 结果如下


2.PNG

这样我们就模仿html-webpack-plugin写出一个类似的插件了。虽然还有其他功能没有实现,只实现了插入script标签的功能。

总结

plugin就是一个类,这个类里面有一个apply方法,我们在这个apply里面根据需求,在对应的钩子中注册我们的想要做的事件。以上是我的理解,如有不对请留言指出讨论,谢谢。

相关文章

网友评论

    本文标题:【webpack进阶系列】plugin的原理探究

    本文链接:https://www.haomeiwen.com/subject/dlxkpctx.html