前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >webpack插件怎么手写

webpack插件怎么手写

作者头像
wade
发布2020-09-14 14:58:38
8840
发布2020-09-14 14:58:38
举报
文章被收录于专栏:coding个人笔记coding个人笔记

webpack插件没什么好说的,用过的都知道怎么配置,只是不知道内部怎么执行。今天学一学插件的一些机制,手写一个插件并不难。

之前介绍过了,webpack本质上是一种事件流机制,核心就是tapable,通过注册事件,触发回调,完成插件在不同生命周期的调用,内部也是通过大量的插件实现的。tapable内部暴露的方法挺多的,主要就是同步和异步,异步分为并行和串行。可以去GitHub上面看看:

https://github.com/webpack/tapable#tapable

这些方法都有用法示例,本来想写写使用方法,发现GitHub上面都有了,就不写了:

代码语言:javascript
复制
const {
 SyncHook,
 SyncBailHook,
 SyncWaterfallHook,
 SyncLoopHook,
 AsyncParallelHook,
 AsyncParallelBailHook,
 AsyncSeriesHook,
 AsyncSeriesBailHook,
 AsyncSeriesWaterfallHook
 } = require("tapable");

在写插件之前,不得不提一下compiler和compilation:

compiler对象代表了完整的 webpack 环境配置,可以访问整个环境。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置(options、loader、plugin等)。使用插件时将收到此 compiler 对象的引用。

compilation 对象代表了一次资源版本构建。在运行过程中,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation提供了很多关键时机的回调供插件做自定义处理时使用。

使用插件就是new一个,所以插件其实就是一个类(构造函数或者class类),内部在prototype定义一个apply方法(会直接调用),并提供compiler,通过compiler提供的hooks注册事件和在相应的回调里面进行操作。而compiler提供的compilation的重要属性是assets,表示所有的静态资源。

关于提供的hook和参数,可以在webpack>lib>Compiler.js搜hooks,其实compiler和compilation都是继承tapable。

关于hooks分别表示什么阶段可以去官网查看:https://www.webpackjs.com/api/compiler-hooks/

还需要明确一下,tapable里面提供的几个hook,同步的(sync开头)注册是tap,异步的(async开头)有tap、tapAsync、tapPromise,后面两个提供了回调函数。

引入自己的插件:

代码语言:javascript
复制
const MyPlugin = require('./plugins/my-plugin.js')

plugins里面使用:

代码语言:javascript
复制
new MyPlugin({
  name: 'wade plugin'
})
my-plugin.js里面:
class MyPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler){
        compiler.hooks.done.tap('MyPlugin', (stats) => {
            console.log('MyPlugin ', this.options);
        });
    }
}
module.exports = MyPlugin;

apply提供了compiler,done是编译完成,同步的调用tap,第一个参数没什么意义,一般写自己插件名字,stats里面对象就多了,有 options、 outputOptions等,可以自己命令行那边看看。

异步的:

代码语言:javascript
复制
class MyPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler){
        compiler.hooks.done.tapAsync('DonePlugin', (stats, callback) => {
            console.log('Hello ', this.options.name);
            setTimeout(() => {console.log(1);}, 1000);
            setTimeout(() => {console.log(2);}, 2000);
            setTimeout(() => {console.log(3);}, 3000);
            setTimeout(() => {
                callback();
            }, 4000)
        });
    }
}
module.exports = MyPlugin;

可以看看控制台,看看效果。

一般自己写插件会在emit和afterEmit进行一些操作,这两个钩子的参数是compilation,里面有assets是静态资源,可以进行操作:

代码语言:javascript
复制
compiler.hooks.emit.tap('DonePlugin', (compilation) => {
   let assets = compilation.assets;
    console.log(assets);
});

比如我想给bundle.js添加一个字符串:

代码语言:javascript
复制
let content = assets['bundle.js'].source();
assets['bundle.js'] = {
    source(){
        return '"build by wade"\r\n' + content
    },
    size(){
        return content.length;
    }
}

打包结果:

比如创建一个文件:

代码语言:javascript
复制
let creatContent = '创建一个文件';
assets['creat.js'] = {
    source(){
        return creatContent
    },
    size(){
        return creatContent.length
    }
}

上面都是没什么意义的操作,只是想表达插件的一些方法,比如可以在文件生成之后进行压缩,或者自动化部署到服务器之类的插件。真正写一些有用的插件还是需要根据具体清空具体实现代码,可能还需要引入一些外部的插件,比如进行请求需要引入ajax或者axios,压缩要引入JSZip等。

(完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 coding个人笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档