This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

The introduction

Webpack is essentially an event flow mechanism, its workflow is to string together plug-ins, and at the heart of it all is tapable, the core of Webpack, The Compiler, which is responsible for compiling, and the Compilation, which is responsible for creating bundles, are instances of the Tapable constructor.

1. What is Tapable

Tapable is the library that WebPack uses to create hooks. Tapable is a process management tool used internally in WebPack to concatenate plugins and improve event flow execution.

In short, Tapable is what WebPack uses to create hooks, so why does Webapck create hooks? Let’s start with the following sentence:

The core functionality of WebPack is achieved by pulling out many plug-ins. Plug-ins are the building blocks of WebPack, and these building blocks influence the direction of the process. The hooks are Tapable, which is similar to the Vue framework lifecycle. Webpack has its own lifecycle in which hooks are sequenced and plugins’ methods mounted on these hooks are executed to perform certain logical processing. Within the plug-in, built entities or built data results are accessible, which makes WebPack highly extensible.

Let’s take a quick look at plugins. Plugins are extensions of functionality that don’t affect existing data.

Throughout the webPack process, we need many plugins to extend the functionality we want at certain times. How do plug-ins do what their code does? By mounting your own methods on webPack’s various hook functions. The Webapck runtime executes the methods of the plugins mounted on the hook itself. So, webapck needs to expose hooks at various times during its run so that plug-ins can mount the methods they want to execute on those hooks. Hooks for webAPck are created using Tapable.

We can think of a WebPack instance as a long wardrobe, representing, from top to bottom, the different processes during the run of WebPack. Webpack creates hangers (hooks) for different periods from top to bottom, for shoes, for clothes, for pants, so we can mount whatever we want on these hooks (various ways of plug-ins).

2. Basic concepts of Tapable

Tapable provides synchronous and asynchronous hooks.

Synchronous hook:

SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
Copy the code

Asynchronous hooks:

  • Asynchronous parallel hooks:

    AsyncParallelHook,
    AsyncParallelBailHook,
    Copy the code
  • Asynchronous serial hook:

     AsyncSeriesHook,
     AsyncSeriesBailHook,
     AsyncSeriesWaterfallHook,
    Copy the code

Hook type parsing

  • Basic hooks (no Bail/Waterfall/Loop): they simply call the functions that each tap passes in.
  • Waterfall hook: will also call each function that tap passes in, but will pass the return value of each function to the next function argument.
  • Hook with Bail: Allows an earlier exit. If one of the functions returns a value other than undefined, the Bail class stops all subsequent function executions.
  • Loop hook: If a function returns a value other than undefined, this Loop calls the callback function until it returns undefined.

There are three plug-in registration methods

  1. Tap: Produces the (event) goods corresponding to the synchronous hook.
  2. TapAsync: produces goods for asynchronous hooks with callback callbacks.
  3. TapPromise: Produces the goods corresponding to the asynchronous hook with the promise callback.

There are three invocation methods corresponding to registration

  1. Call: Calls the registered synchronous hook.
  2. CallAsync: Calls a registered asynchronous hook with a callback function.
  3. Promise: Calls a registered asynchronous hook with a PROMISE callback.

3. Basic example of Tapable

Use of synchronous hooks:

SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
Copy the code

SyncHook:

Const {SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook,} = require ('tapable'); // Initialize hook, const syncHK = new SyncHook(['name', 'age']) Tap ('plugin1 ', (name, age)=> {console.log('plugin1:', name, age); } ) syncHK.tap('plugin2:', (name, age) => { console.log('plugin2:', name, age); }) // Consume synchk.call ('zack', 18)Copy the code

SyncBailHook:

Tap ('a', (name, age) => {console.log('a:', name, age); }) bailHook.tap('b', (name, age) => { console.log('b:', name, age); return 'b' }) bailHook.tap('c', (name, age) => { console.log('c:', name, age); }) // consume bailhook.call ('lili', 20) // result2 // a: lili 20 // b: lili 20Copy the code

SyncWaterfallHook:

Const waterFallHK = new SyncWaterfallHook(['name', 'age']) age) => { console.log('one:', name, age); return age }) waterFallHK.tap('two', (age) => { console.log('two:', age); }) // consume waterFallHK. Call ('pilipili', 25) // result3 // one: pilipili 25 // two: 25Copy the code

SyncLoopHook:

Let num = 20 const loopHK = new SyncLoopHook (['name', 'age']) age) => { console.log('1:', name, age); if(num>18) { num--; console.log('num:', num); return num } }) loopHK.tap('2', (name, age) => { console.log('2', name, age); // result4: // 1: kiki 21 // num: 19 // 1: kiki 21 // num: 18 // 1: kiki 21 // num: 18 // 1: kiki 21 // 2 kiki 21Copy the code

Use of asynchronous parallel hooks:

Asynchronous hook registration generally does not use tap, otherwise it will end up serial, using the latter two.

All tasks are executed together, and the first to complete the output is the first. After all tasks are executed, the callback is executed.

AsyncParallelHook,
AsyncParallelBailHook,
Copy the code

AsyncParallelHook:

Asynchronous parallel execution, callAsync or promise is executed only after all registered events have been executed.

const asyncPHK = new AsyncParallelHook(['name'])

asyncPHK.tapAsync('print1', (name, callbackName) => {
    setTimeout(() => {
        console.log('1', name);
        callbackName('hhhh',  '11')
    }, 2000)
})

asyncPHK.callAsync('lili', (err, res) => {
    console.log('err', err);
    console.log('res', res);
})
// 1 lili
// err hhhh
// res undefined
Copy the code
const asyncPHK2 = new AsyncParallelHook(['name'])
asyncPHK2.tapPromise('1'.(name) = > {
    // tapPromise needs to return a Promise
    return new Promise((resolve, reject) = > {

        setTimeout(() = > {
            console.log(name, 1);
            resolve('11')},2000)

    })
})
asyncPHK2.tapPromise('2'.(name) = > {
    // tapPromise needs to return a Promise
    return new Promise((resolve, reject) = > {

        setTimeout(() = > {
            console.log(name, 2);
            resolve('22')},1000)})})/ / triggers
asyncPHK2.promise('tapPromise')
.then(res= > {
    console.log('over', res);
})
.catch(err= > {
    console.log('error', err);
})
// tapPromise 2
// tapPromise 1
// over undefined
Copy the code

AsyncParallelBailHook:

const asyncPBailHK = new AsyncParallelBailHook(['name'])

asyncPBailHK.tapAsync('1', (name, callBK) => {
    setTimeout(() => {
        console.log(name, 01);
        callBK('001')
    }, 2000)
})
asyncPBailHK.tapAsync('2', (name, callBK) => {
    setTimeout(() => {
        console.log(name, 02);
        callBK('002')
    }, 1000)
})
asyncPBailHK.callAsync('bail', (res) => {
    console.log(res);
})
// bail 2
// bail 1
// 001
Copy the code
const asyncPBailHK2 = new AsyncParallelBailHook(['name'])

asyncPBailHK2.tapPromise('2'.(name) = > {
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log(name, 22);
            resolve('222')},2000)
    })
})
asyncPBailHK2.tapPromise('2'.(name) = > {
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log(name, 23);
            resolve('223')},1000)
    })
})
asyncPBailHK2.promise('bail222')
.then(res= > {
    console.log('over', res);
})
.catch(err= > {
    console.log('err', err);
})
Copy the code

Use of asynchronous serial hooks:

AsyncSeriesHook,

AsyncSeriesBailHook,

AsyncSeriesWaterfallHook,

Same as above.