navigation

[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise

[react] Hooks

[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI

[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend

Vue. NextTick and VM.$nextTick

Front knowledge

Some words

// All Hook constructors take one optional argument,whichIs a list of argument names as strings. // All Hook constructors take an optional argument, which is an array of argument name strings. // The best practice is to expose all hooks of a classinA hooks property // It is a best practice to expose all hooks of the class in the hooks attributeCopy the code

Publish subscribe model

  • Publisher: Publisher
  • Subscriber: SUBSCRIBE
  • Mediation: Topic/Event Channel
  • (Mediation) both receives the message published by (publisher) and sends the message to (subscriber) subscribed to the event
    • The intermediary needs to store the corresponding information based on the event
    • Complete decoupling of (publisher) and (subscriber) through (mediation object)
  • Publish subscribe pattern code implementation – ES5
<script> const pubsub = {}; // Subscribe, unSubscribe, publish, etc.function(pubsub) { const topics = {}; // key: is a different event // value: is an array of subscribers subscribed to the event // event_name: [{fname: Fn. Name, fn}] // subscribe pubsub.subscribe =function(eventName, fn) {// The subscriber object array does not exist, create // the subscriber object array does exist, add the subscriber object to the arrayif(! Topics [eventName]) {topics[eventName] = []} topics[eventName].push({fname: fn.name, fn})} pubsub.publish =function(eventName, params) {
      if(! topics[eventName].length) {return} // Pull the update function from all the subscriber and execute the topic [eventName].foreach ((item, index) => {item.fn(params)})} // unSubscribe pubsub.unsubscribe =function(eventName, fname) {
      if(! topics[eventName]) {return
      }
      topics[eventName].forEach((item, index) => {
        if(item. Fname === fname) {topics[eventName].splice(index, 1)}})(pubsub) (item. Fname === fname) {topics[eventName].splice(index, 1)}})(pubsub)function goFn1(params) {
    console.log('goFn1', params)
  }
  function goFn2(params) {
    console.log('goFn2', params)
  }

  pubsub.subscribe('go'GoFn1) // subscribe to pubsub.subscribe('go', goFn2)

  pubsub.publish('go'.'1111111') // Publish pubsub.unsubscribe ('go', gofn2.name) // Unsubscribe pubsub.publish('go'.'22222')
</script>
Copy the code
  • Publish subscribe pattern code implementation – ES6
<script> class PubSub {// PubSub classconstructor() {this.topics = {} // Object to maintain the relationship between event and subscriber // event <-> subscriber array} subscribe(eventName, fn) {//if(! this.topics[eventName]) { this.topics[eventName] = [] } this.topics[eventName].push({ fname: Name, fn})} publish(eventName, params) {// publishif(! this.topics[eventName].length) {return} this.topics[eventName].foreach ((item, index) => {item.fn(params)})} unSubscribe(eventName, fname) {// unSubscribeif(! this.topics[eventName]) {return
      }
      this.topics[eventName].forEach((item, index) => {
        if (item.fname === fname) {
          this.topics[eventName].splice(index, 1)
        }
      })
    }
  }

  const pubsub = new PubSub()

  function goFn1(params) {
    console.log('goFn1', params)
  }
  function goFn2(params) {
    console.log('goFn2', params)
  }

  pubsub.subscribe('go', goFn1)
  pubsub.subscribe('go', goFn2)

  pubsub.publish('go'.'1') // Publish pubsub.unsubscribe ('go', gofn2.name) // Unsubscribe pubsub.publish('go'.'2')
</script>
Copy the code

Tapable

  • At its core is the publish-subscribe pattern
  • Installation:npm install tapable -S

SyncHook

  • Synchronous publish-and-subscribe pattern
  • const synchook = new SyncHook(['params'])
  • Synchok.tap () // subscribe, register
  • Synchok.call () // Publish, execute
const { SyncHook } = require('tapable')

class Work {
  constructor() {
    this.hooks = {
      city: new SyncHook(['who'}} tap(eventName) {this.hooks. City.tap (eventName, eventName, eventName, eventName) {this.hooks.function(who){console.log(who, eventName)})publish() {
    this.hooks.city.call('woow_wu7')
  }
}
const work = new Work()
work.tap('chongqing') // Subscribe to work.tap('hangzhou') work.publish() // PublishCopy the code
--------- const {SyncHook} = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new SyncHook(['name']) // Synchronous hooks that take an argument in the argument function of the.tap() function}}tap() {
    this.hook.arch.tap('react', (name) => {// the name argument is the console.log(passed in.call()).'react', name)
    })
    this.hook.arch.tap('vue', (name) => {
      console.log('vue', name)
    })
  }
  publish() {
    this.hook.arch.call('woow_wu7')
  }
}

const lesson = new Lesson(['name']) lesson.tap() // Register lesson.publish() // publishCopy the code

SyncHook simulation implementation

class SyncHook {
  constructor() {this.observers = [] // Observers are an array of objects, including the event name, and the fn task function // [{event: eventName, fn: Fn}]} // If you are obsered by observers, you are obsered by observers. If you are obsered by observers, you are obsered by observers. EventName, fn})} // Execute the fn function call(... params) { this.observers.forEach(item => item.fn(item.event, ... params)) } } const synchook = new SyncHook(['params'])

synchook.tap('react'.function(eventName, name) {
  console.log(eventName, name)
})
synchook.tap('vue'.function(eventName, name) {
  console.log(eventName, name)
})

synchook.call('woow_wu7')
Copy the code

SyncBailHook

  • The SyncBailHook provides a mechanism for listening to functions in the subscriber array (terminating execution)
  • When the listening function in.tap() returns a value of (! Call () function when undefined
let { SyncBailHook } = require('tapable');

class Lesson {
  constructor() {
    this.hooks = {
      arch: new SyncBailHook(['name'])}; }tap() {
    this.hooks.arch.tap('vue'.function(name) {
      console.log('vue', name);
      return 'SyncBailHook 'when the return value is! When undefined, it stops executing '; / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- SyncBailHook when. Tap () parameters to monitor function return value is (! Undefined) is no longer executed below //returnUndefined ---------- is not affected if the return value is undefined, because the default return value of the function is undefined}); this.hooks.arch.tap('react'.function(name) {
      console.log('react', name);
    });
  }
  
  start() {
    this.hooks.arch.call('woow_wu7'); }}let lesson = new Lesson();
lesson.tap();
lesson.start();

Copy the code

SyncBailHook simulation implementation

class SyncBailHook {
  constructor() {this.observers = [] // Observers are an array of objects, including the event name, and the fn task function // [{event: eventName, fn: Fn}]} // If you are obsered by observers, you are obsered by observers. If you are obsered by observers, you are obsered by observers. EventName, fn})} // Execute the fn function call(... params) { // this.observers.forEach(item => item.fn(item.event, ... params))let res = undefined;
    for(leti = 0; i < this.observers.length; I++) {const currentObj = this. Observers [I] / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- used for many times, Res = currentobj.fn (currentobj.event,... params)if(res ! = = undefined) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- circular array when do judgment, if the function return value is (! Undefined) pops out of the.call() functionreturn
      }
    }
  }
}

const syncBailHook = new SyncBailHook(['params'])

syncBailHook.tap('react'.function(eventName, name) {
  console.log(eventName, name)
  return 'stop'
})
syncBailHook.tap('vue'.function(eventName, name) {
  console.log(eventName, name)
})

syncBailHook.call('woow_wu7')
Copy the code

SyncWaterfallHook

let { SyncWaterfallHook } = require('tapable'); // SyncWaterfallHook passes the callback of the previous.tap() function as (argument) to the next.tap() callback function class Lesson {constructor() {
    this.hooks = {
      arch: new SyncWaterfallHook(['name'])}; } // Register the listening functiontap() {
    
    this.hooks.arch.tap('vue'.function(name) {
      console.log('vue', name);
      return 'the vue is good'; / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- SyncBailHook when the return value is! If undefined, the execution will not continuereturnUndefined ---------- is not affected if the return value is undefined, because the default return value of the function is undefined}); this.hooks.arch.tap('react'.function(name) {
      console.log('react', name); // Where name is the value returned by the last callback, vue is goodreturn 'the react well'
    });

    this.hooks.arch.tap('node'.function(name) {
      console.log('node', name); // The name is the return value of the last callback, i.e. react is good}); }start() {
    this.hooks.arch.call('woow_wu7'); }}letlesson = new Lesson(); lesson.tap(); lesson.start(); // vue woow_wu7 // react vue // node reactCopy the code

SyncWaterfallHook simulation implementation

  • Iterate primarily using the reduce() method of arrays
class SyncWaterfallHook {
  constructor() {this.observers = [] // Observers are an array of objects, including the event name, and the fn task function // [{event: eventName, fn: Fn}]} // If you are obsered by observers, you are obsered by observers. If you are obsered by observers, you are obsered by observers. EventName, fn})} // Execute the fn function call(... params) { // this.observers.forEach(item => item.fn(item.event, ... params)) const [first, ...rest] = this.observers const fisrtRes = this.observers[0].fn(... params) rest.reduce((a, b) => {returnB. fn(a)}, fisrtRes) // First time: a = fisrtRes, b = the first member of the array // Second time: a = the first return value b.fn(a), b = the second member of the array //... } } const syncWaterfallHook = new SyncWaterfallHook(['params'])

syncWaterfallHook.tap('react'.function(name) {
  console.log('react', name)
  return 'react ok'
})
syncWaterfallHook.tap('vue'.function(name) {
  console.log('vue', name)
  return 'vue ok'
})
syncWaterfallHook.tap('node'.function(name) {
  console.log('node', name)
})

syncWaterfallHook.call('woow_wu7')

// react woow_wu7
// vue react ok
// node vue ok
Copy the code

SyncLoopHook

  • When the currently listening function returns (! Undefined) is executed multiple times until the current listener returns undefined and the next function is stopped
let { SyncLoopHook } = require('tapable'); // SyncLoopHook // when the callback to.tap() returns! If the return value is undefined, the execution will be terminated and the next.tap() class Lesson {constructor() {
    this.hooks = {
      arch: new SyncLoopHook(['name'])}; } index = 0; // this is the instance object. // this is the instance object. // this is the instance object The reason tap() can call this.index is because tap is called on the less instance, so this means that the instance can get this.index // to register the listener functiontap() {
    const that = this;
    this.hooks.arch.tap('react'.function(name) {
      console.log('react', name);
      return ++that.index === 3 ? undefined : 'the react well'; // If the return value is undefined, the tap function is executed until it is undefined}); this.hooks.arch.tap('node'.function(name) {
      console.log('node', name);
    });
  }
  start() {
    this.hooks.arch.call('woow_wu7'); }}let lesson = new Lesson();
lesson.tap();
lesson.start();

Copy the code

SyncLoopHook simulation implementation

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
</head>
<body>
  <script>
class SyncLoopHook {
  constructor() { this.observers = []; // Observers are an array of observer objects, including the event name, and the fn task function // [{event: eventName, fn: Fn}]} // If you are obsered by observers, you are obsered by observers. If you are obsered by observers, you are obsered by observers. EventName, fn})} // Execute the fn function call(... params) { this.observers.forEach(item => {let res;
      do{ res = item.fn(... params) }while(res ! == undefined); }) } } const syncLoopHook = new SyncLoopHook(['params'])

let count = 0;

syncLoopHook.tap('react'.function(name) {
  console.log('react', name)
  return ++count === 3 ? undefined : 'go on'}) // Note that the scope of a function is the object in which the function was defined // When called multiple times in the.call() method, the count is increased, because the scope of both functions and variables is determined when declared syncloophook.tap ())'vue'.function(name) {
  console.log('vue', name)
})
syncLoopHook.tap('node'.function(name) {
  console.log('node', name)
})

syncLoopHook.call('woow_wu7')
  </script>
</body>
</html>
Copy the code

AsyncParallelHook – through.tapAsync(_, (name, cb)=>.callAsync(_, ()=>{})call

  • Asynchronous parallel hooks
  • The word “parallel” means parallel
  • Series: Serial
let { AsyncParallelHook } = require('tapable'); // AsyncParallelHook = AsyncParallelHook; // parallel; // series;constructor() {
    this.hooks = {
      arch: new AsyncParallelHook(['name'])}; } // Register the listening functiontap() {
    this.hooks.arch.tapAsync('react'.function(name, cb) {// ----------- tapAsync Asynchronous registrationsetTimeout(function () {
        console.log('react', name); cb(); / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- cb} () has been completed, 1000)}); this.hooks.arch.tapAsync('node'.function (name, cb) {
      setTimeout(function () {
        console.log('node', name); cb(); / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- only a cb () are not performed, in callAsync () in the callback function will not trigger}, 1000)}); }start() {
    this.hooks.arch.callAsync('woow_wu7'.function() {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- callAsync asynchronous execution console. The log ('end')}); }}let lesson = new Lesson();
lesson.tap();
lesson.start();


Copy the code

AsyncParallelHook simulation implementation

class AsyncParallelHook {
  constructor() { this.observers = []; } tapAsync(eventName, fn) { this.observers.push({ event: eventName, fn }) } callAsync(... Params) {const callbackFn = params.pop(); // Take the last argument of callAsync();shiftPush (), unshift() returns the size of the array, changing the size of the array const that = this // fixes thissletCount = 0 // Counts the number of cb callback executionsfunction done() {
      count++;
      if(count === that.observers.length) {// Ensure that the cb() callback is executed in each.tap() // CallbackFn ()}} this.observers.foreach ((item, index) => {item.fn(... params,done) / /done}} Const asyncParallelHook = new asyncParallelHook (['params'])

asyncParallelHook.tapAsync('react'.function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb()
  }, 1000);
})
asyncParallelHook.tapAsync('vue'.function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb()
  }, 1000);
})
asyncParallelHook.tapAsync('node'.function(name, cb) {
  setTimeout(() => {
    console.log('node', name)
    cb()
  }, 1000);
})

asyncParallelHook.callAsync('woow_wu7'.function() {
  console.log('end')})Copy the code

AsyncParallelHook – through.tapPromise().promise().then()call

const { AsyncParallelHook } = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new AsyncParallelHook(['name'])}}tap() {
    this.hook.arch.tapPromise('react', name => {
      return new Promise((resolove) => {
        setTimeout(() => {
          console.log('react', name)
          return resolove()
        }, 1000);
      })
    })
    this.hook.arch.tapPromise('vue', name => {
      return new Promise((resolove) => {
        setTimeout(() => {
          console.log('react', name)
          returnresolove() }, 1000); })})}publish() {
    this.hook.arch.promise('woow_wu7').then(() => {
      console.log('end')
    })
  }
}

const lesson = new Lesson(['name']) lesson.tap() // Register lesson.publish() // publishCopy the code

AsyncParallelHook – through.tapPromise().promise().then()Call – mock implementation

class AsyncParallelHook {
  constructor() { this.observers = []; } tapPromise(eventName, fn) { this.observers.push({ event: eventName, fn }) } promise(... params) { const promiseArr = this.observers.map(item => item.fn(... params))returnPromise.all(promiseArr) // returns a promise.all () function // All resolve() is resolved () // A rejectI() is rejected ()}} const asyncParallelHook = new AsyncParallelHook(['params'])

asyncParallelHook.tapPromise('react'.function (name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('react', name)
      return resolve()
    }, 1000);
  })
})
asyncParallelHook.tapPromise('vue'.function (name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('react', name)
      return resolve()
    }, 1000);
  })
})
asyncParallelHook.promise('node').then(function (name) {
  console.log('end')})Copy the code

AsyncSeriesHook – Asynchronous serial hook

  • The next.tap() is not executed until the previous.tap() has been executed, and the callback function in.callAsync() is not executed until all cb() in.tap() has been executed
const { AsyncSeriesHook } = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new AsyncSeriesHook(['name'])}}tap() {
    this.hook.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name)
        cb()
      }, 1000);
    })
    this.hook.arch.tapAsync('vue', (name, cb) => {
      setTimeout(() => {
        console.log('vue', name) cb() }, 1000); })}publish() {
    this.hook.arch.callAsync('woow_wu7', () => {
      console.log('end')
    })
  }
}

const lesson = new Lesson(['name'])

lesson.tap()
lesson.publish()
Copy the code

AsyncSeriesHook simulation implementation

class AsyncSeriesHook {
  constructor() { this.observers = [] } tapAsync(name, fn) { this.observers.push({ event_name: name, fn }) } callAsync(... params) { const lastCallback = params.pop()let index = 0;
    const that = this;
    function next() {
      if (index === that.observers.length) {
        returnLastCallback () // if not, never execute lastCallback()} that.observers[index++].fn(... Pausing [0] // Then index = index+ 1 => index = 1 // recurred to next() method, } next()}} Const asyncSeriesHook = new asyncSeriesHook (['name'])

asyncSeriesHook.tapAsync('react'.function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb()
  }, 1000);
})
asyncSeriesHook.tapAsync('vue'.function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb()
  }, 1000);
})
asyncSeriesHook.callAsync('woow_wu7'.function() {
  console.log('end')})Copy the code

AsyncSeriesWaterfullHook

const { AsyncSeriesWaterfallHook } = require('tapable')

class Lesson {
  constructor() {
    this.hook = {
      arch: new AsyncSeriesWaterfallHook(['name'])}}tap() {
    this.hook.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name)
        cb(null, 'next-react') // When the first parameter is a BooleanfalseIs passed'next-react'As an argument to the callback function in the next argument to.tapAsynce() // when the first argument is a Boolean valuetrue// Note: although the next.tapAsynce() // is interrupted, the callAsync second argument callback is executed because cb() is called and the total number of.tapAsynce() is printed'end'
      }, 1000);
    })
    this.hook.arch.tapAsync('vue', (name, cb) => {
      setTimeout(() => {
        console.log('vue', name)
        cb(null, 'next-vue')
        // 如果是cb('null'.'next-vue'The Boolean value of the first parameter istrueTapAsynce ()}, 1000); })}publish() {
    this.hook.arch.callAsync('woow_wu7', () => {
      console.log('end')
    })
  }
}

const lesson = new Lesson(['name'])

lesson.tap()
lesson.publish()
Copy the code

AsyncSeriesWaterfullHook simulation implementation

class AsyncSeriesWaterfallHook {
  constructor() { this.observers = [] } tapAsync(name, fn) { this.observers.push({ event_name: name, fn }) } callAsync(... params) { const lastCallback = params.pop()let index = 0;
    const that = this;
    function next(err, data) {
      let task = that.observers[index]
      if(! Task | | err | | index. = = = that observers. Length) {/ / if the task does not exist / / / / or err exist or index reached a maximum length, in fact can not judge, If the index is beyond that, Observers. length, the task will not exist. If the conditions are observers.length, the task will not existreturn lastCallback()
      }
      if(index === 0) { task.fn(... params, next) }else {
        task.fn(data, next)
      }
      index++
    }
    next()
  }
}

const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(['name'])

asyncSeriesWaterfallHook.tapAsync('react'.function(name, cb) {
  setTimeout(() => {
    console.log('react', name)
    cb(null, 'next-react')}, 1000); }) asyncSeriesWaterfallHook.tapAsync('vue'.function(name, cb) {
  setTimeout(() => {
    console.log('vue', name)
    cb(null, 'next-vue')}, 1000); }) asyncSeriesWaterfallHook.callAsync('woow_wu7'.function() {
  console.log('end')})Copy the code

data

Tapable github.com/webpack/tap… Tapable juejin. Cn/post / 684490… Tapable juejin. Cn/post / 684490…