webpack核心库tapable探究

246 阅读3分钟

引子

webpack核心库tapable的源码探究,Tapable是webpack中重要一环,它的工作就是将各个插件串联起来,确保在对应的时机执行对应的loader或者plugin。Tapable的实现类似于promise redux等,核心原理依赖于发布订阅模式。



我们不是一比一复刻源码,而是对于源码的某一些核心方法进行自己的实现,具体如下
\

 1.SyncBaikHook

先看使用示例:

const { SyncBailHook } = require('tapable')
class Lesson {
  constructor() {
    this.hook = {
        //这里的['name'],代表call可传几个参数给tap的第二个参数
        //['name','age'],表示this.hook.arch.call('小明',18)
      arch: new SyncBailHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tap('语文', (name) => {
      console.log(name, '---语文')
    })
    this.hook.arch.tap('数学', (name) => {
      console.log(name, '---数学')
    })
  }
  start() {
    this.hook.arch.call('小明')
  }
}
let l = new Lesson()
l.tap()
l.start()

执行结果:


如果上一次执行有返回,终止执行\

class Lesson {
  constructor() {
    this.hook = {
      arch: new SyncBailHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tap('语文', (name,) => {
      console.log(name, '---语文')
      ++ return '语文学习不达标'
     })
    this.hook.arch.tap('数学', (name,) => {
      console.log(name, '---数学')
     })
  }
  start() {
    this.hook.arch.call('小明')
   }
}
let l = new Lesson()
l.tap()
l.start()

执行结果:


实现一个SyncBailHook\

class SyncBailHook1{
  constructor(name) {
    this.tasks=[]
  }
  tap(name,tasks) {
    this.tasks.push(tasks)
  }
  call(...args) {
    let ret;
    let index = 0;
    do {
    //拿到每一次注册函数的执行结果,有返回,则终止执行
      ret=this.tasks[index++](...args)
    } while (ret===undefined && index<this.tasks.length)
  }
}

\

 2.SyncWaterHook

使用示例:\

const { SyncWaterfallHook } = require('tapable')
class Lesson {
  constructor() {
    this.hook = {
      arch: new SyncWaterfallHook(['name'])
    }
}
  tap() {
    this.hook.arch.tap('语文', (name,) => {
      console.log(name, '---语文')
      return '语文考了满分'
  })
   this.hook.arch.tap('数学', (name,) => {
     console.log(name, '---数学')
     return '数学考了满分'
  })
   this.hook.arch.tap('英语', (name,) => {
     console.log(name,'---英语')
  })
}
  start() {
    this.hook.arch.call('小明')
  }
}

let l = new Lesson()
l.tap()
l.start()

执行结果:


实现一个SyncWaterHook\

class SyncWaterfallHook{
  constructor(name) {
    this.tasks=[]
  }
  tap(name,tasks) {
    this.tasks.push(tasks)
   }
  call(...args) {
    const [first, ...ohthers] = this.tasks
    let ret = first(...args)
    //利用reduce进行处理,acc类似于累积器,收集到上一次执行结果,传递给下一次执行
    ohthers.reduce((acc, cur) => {
       return cur(acc)
     },ret)
  }
}

 3.AsyncParalleHook

使用示例:\

const { AsyncParallelHook } = require('tapable')
class Lesson {
  constructor() {
    this.index = 0;
    this.hook = {
      arch: new AsyncParallelHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapAsync('语文', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---语文')
        cb()
        }, 1000);
    })
    this.hook.arch.tapAsync('数学', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---数学')
        cb()
      },2000)
    })
    this.hook.arch.tapAsync('英语', (name,cb) => {
       setTimeout(() => {
         console.log(name, '---英语')
         cb()
      },1000)

    })
  }
  start() {
    this.hook.arch.callAsync('小明', () => {
      console.log('学完了')
    })
  }
}

let l = new Lesson()
l.tap()
l.start()

执行结果:


实现一个AsyncParallelHook\

class AsyncParallelHook{
    constructor(name) {
      this.tasks=[]
    }
    tapAsync(name,tasks) {
      this.tasks.push(tasks)
    }
    callAsync(...args) {
      let finalCallback = args.pop() //取出最终函数
      let index = 0;
      //tap的回调函数,当每一个注册到tasks数组里面函数执行完成,调用callAsync的回调(即第二个参数)
      const done = () => {
        index++
        if (index === this.tasks.length) {
          finalCallback()
        }
      }
      this.tasks.forEach((task) => {
        task(...args,done)
      })
    }
}

\

4.AsyncSeriesHook

使用示例1:\

const { AsyncSeriesHook } = require('tapable')
class Lesson {
  constructor() {
    this.index = 0;
    this.hook = {
      arch: new AsyncSeriesHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapAsync('语文', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---语文')
        cb()
        }, 1000);
    })
    this.hook.arch.tapAsync('数学', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---数学')
        cb()
      },2000)
    })
    this.hook.arch.tapAsync('英语', (name,cb) => {
       setTimeout(() => {
         console.log(name, '---英语')
         cb()
      },1000)

    })
  }
  start() {
    this.hook.arch.callAsync('小明', () => {
      console.log('学完了')
    })
  }
}

let l = new Lesson()
l.tap()
l.start()

执行结果:\



实现一个AsyncSeriesHook:

class AsyncSeriesHook{
    constructor(name) {
      this.tasks=[]
    }
    tapAsync(name,tasks) {
      this.tasks.push(tasks)
    }
    callAsync(...args) {
      let finalCallback=args.pop()
      let index = 0;
      //并发串行,这里实现的回调,保证了在前一个task执行完成后,在调起下一个
      let next = () => {
        if(this.tasks.length===index) return finalCallback()
        let task=this.tasks[index++]
        task(...args,next)
      }
      next()
    }
}

\

使用示例2:\

const { AsyncSeriesHook } = require('tapable')
class Lesson {
  constructor() {
    this.index = 0;
    this.hook = {
      arch: new AsyncSeriesHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapPromise('语文', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(name, '---语文')
          resolve()
        }, 1000);
      })
    })
    this.hook.arch.tapPromise('数学', (name) => {
       return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(name, '---数学')
          resolve()
        },2000)
      })

    })
    this.hook.arch.tapPromise('英语', (name) => {
       return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(name, '---英语')
          resolve()
        },1000)
      })
    })
  }
  start() {
    this.hook.arch.promise('小明').then(() => {
      console.log('学完啦')
    })
  }
}

let l = new Lesson()
l.tap()
l.start()

\


实现一个AsyncSeriesHook\

class AsyncSeriesHook{
    constructor(name) {
      this.tasks=[]
    }
    tapPromise(name,tasks) {
      this.tasks.push(tasks)
    }
    promise(...args) {
      const [first, ...others] = this.tasks
      //这里还是利用reduce,把后一个task放到前一个task的.then函数里面,保证了执行顺序
      return  others.reduce((acc, cur) => {
        return  acc.then(() => cur(...args))
      },first(...args))
    }
}

 

5.AsyncSeriesWaterfallHook

使用示例:\

const { AsyncSeriesWaterfallHook } = require('tapable')
class Lesson {
  constructor() {
    this.index = 0;
    this.hook = {
      arch: new AsyncSeriesWaterfallHook(['name'])
    }
  }
  tap() {
    this.hook.arch.tapAsync('语文', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---语文')
        //回调第一个参数标识是否出错,终止执行,有值标识出错
        cb(null,100)
        }, 1000);
    })
    this.hook.arch.tapAsync('数学', (name,cb) => {
      setTimeout(() => {
        console.log(name, '---数学')
         cb(null,99)
      },2000)
    })
    this.hook.arch.tapAsync('英语', (name,cb) => {
       setTimeout(() => {
         console.log(name, '---英语')
         cb(null,59)
      },1000)
    })
  }
  start() {
    this.hook.arch.callAsync('小明', () => {
      console.log('学完了')
    })
  }
}

let l = new Lesson()
l.tap()
l.start()

执行结果:



实现一个AsyncSeriesWaterfallHook:\

class AsyncSeriesWaterfallHook{
    constructor(name) {
      this.tasks=[]
    }
    tapAsync(name,tasks) {
      this.tasks.push(tasks)
    }
    callAsync(...args) {
      let finalCallback=args.pop()
      let index = 0;
      let next = (err,data) => {
        let task = this.tasks[index]
        //如果出错,直接执行callAsync的回调函数
        if (err) {
         return  finalCallback()
        }
        //第一次执行,传递callAsync的参数
        if (index === 0) {
            task(...args,next)
          } else {
          //后面每次执行,传递tapAsync传递的回调参数
            task(data,next)
          }
        index++
      }
      next(null,...args)
    }
}