提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

271 lines
7.1 KiB

  1. # Hookable
  2. [![npm version][npm-version-src]][npm-version-href]
  3. [![npm downloads][npm-downloads-src]][npm-downloads-href]
  4. [![bundle][bundle-src]][bundle-href]
  5. [![Codecov][codecov-src]][codecov-href]
  6. [![License][license-src]][license-href]
  7. Awaitable hooks system.
  8. ## Install
  9. Using yarn:
  10. ```bash
  11. yarn add hookable
  12. ```
  13. Using npm:
  14. ```bash
  15. npm install hookable
  16. ```
  17. ## Usage
  18. **Method A: Create a hookable instance:**
  19. ```js
  20. import { createHooks } from 'hookable'
  21. // Create a hookable instance
  22. const hooks = createHooks()
  23. // Hook on 'hello'
  24. hooks.hook('hello', () => { console.log('Hello World' )})
  25. // Call 'hello' hook
  26. hooks.callHook('hello')
  27. ```
  28. **Method B: Extend your base class from Hookable:**
  29. ```js
  30. import { Hookable } from 'hookable'
  31. export default class FooLib extends Hookable {
  32. constructor() {
  33. // Call to parent to initialize
  34. super()
  35. // Initialize Hookable with custom logger
  36. // super(consola)
  37. }
  38. async someFunction() {
  39. // Call and wait for `hook1` hooks (if any) sequential
  40. await this.callHook('hook1')
  41. }
  42. }
  43. ```
  44. **Inside plugins, register for any hook:**
  45. ```js
  46. const lib = new FooLib()
  47. // Register a handler for `hook2`
  48. lib.hook('hook2', async () => { /* ... */ })
  49. // Register multiply handlers at once
  50. lib.addHooks({
  51. hook1: async () => { /* ... */ },
  52. hook2: [ /* can be also an array */ ]
  53. })
  54. ```
  55. **Unregistering hooks:**
  56. ```js
  57. const lib = new FooLib()
  58. const hook0 = async () => { /* ... */ }
  59. const hook1 = async () => { /* ... */ }
  60. const hook2 = async () => { /* ... */ }
  61. // The hook() method returns an "unregister" function
  62. const unregisterHook0 = lib.hook('hook0', hook0)
  63. const unregisterHooks1and2 = lib.addHooks({ hook1, hook2 })
  64. /* ... */
  65. unregisterHook0()
  66. unregisterHooks1and2()
  67. // or
  68. lib.removeHooks({ hook0, hook1 })
  69. lib.removeHook('hook2', hook2)
  70. ```
  71. **Triggering a hook handler once:**
  72. ```js
  73. const lib = new FooLib()
  74. const unregister = lib.hook('hook0', async () => {
  75. // Unregister as soon as the hook is executed
  76. unregister()
  77. /* ... */
  78. })
  79. ```
  80. ## Hookable class
  81. ### `constructor()`
  82. ### `hook (name, fn)`
  83. Register a handler for a specific hook. `fn` must be a function.
  84. Returns an `unregister` function that, when called, will remove the registered handler.
  85. ### `hookOnce (name, fn)`
  86. Similar to `hook` but unregisters hook once called.
  87. Returns an `unregister` function that, when called, will remove the registered handler before first call.
  88. ### `addHooks(configHooks)`
  89. Flatten and register hooks object.
  90. Example:
  91. ```js
  92. hookable.addHooks({
  93. test: {
  94. before: () => {},
  95. after: () => {}
  96. }
  97. })
  98. ```
  99. This registers `test:before` and `test:after` hooks at bulk.
  100. Returns an `unregister` function that, when called, will remove all the registered handlers.
  101. ### `async callHook (name, ...args)`
  102. Used by class itself to **sequentially** call handlers of a specific hook.
  103. ### `callHookWith (name, callerFn)`
  104. If you need custom control over how hooks are called, you can provide a custom function that will receive an array of handlers of a specific hook.
  105. `callerFn` if a callback function that accepts two arguments, `hooks` and `args`:
  106. - `hooks`: Array of user hooks to be called
  107. - `args`: Array of arguments that should be passed each time calling a hook
  108. ### `deprecateHook (old, name)`
  109. Deprecate hook called `old` in favor of `name` hook.
  110. ### `deprecateHooks (deprecatedHooks)`
  111. Deprecate all hooks from an object (keys are old and values or newer ones).
  112. ### `removeHook (name, fn)`
  113. Remove a particular hook handler, if the `fn` handler is present.
  114. ### `removeHooks (configHooks)`
  115. Remove multiple hook handlers.
  116. Example:
  117. ```js
  118. const handler = async () => { /* ... */ }
  119. hookable.hook('test:before', handler)
  120. hookable.addHooks({ test: { after: handler } })
  121. // ...
  122. hookable.removeHooks({
  123. test: {
  124. before: handler,
  125. after: handler
  126. }
  127. })
  128. ```
  129. ### `removeAllHooks`
  130. Remove all hook handlers.
  131. ### `beforeEach (syncCallback)`
  132. Registers a (sync) callback to be called before each hook is being called.
  133. ```js
  134. hookable.beforeEach((event) => { console.log(`${event.name} hook is being called with ${event.args}`)}`)
  135. hookable.hook('test', () => { console.log('running test hook') })
  136. // test hook is being called with []
  137. // running test hook
  138. await hookable.callHook('test')
  139. ```
  140. ### `afterEach (syncCallback)`
  141. Registers a (sync) callback to be called after each hook is being called.
  142. ```js
  143. hookable.afterEach((event) => { console.log(`${event.name} hook called with ${event.args}`)}`)
  144. hookable.hook('test', () => { console.log('running test hook') })
  145. // running test hook
  146. // test hook called with []
  147. await hookable.callHook('test')
  148. ```
  149. ### `createDebugger`
  150. Automatically logs each hook that is called and how long it takes to run.
  151. ```js
  152. const debug = hookable.createDebugger(hooks, { tag: 'something' })
  153. hooks.callHook('some-hook', 'some-arg')
  154. // [something] some-hook: 0.21ms
  155. debug.close()
  156. ```
  157. ## Migration
  158. ### From `4.x` to `5.x`
  159. - Type checking improved. You can use `Hookable<T>` or `createHooks<T>()` to provide types interface **([c2e1e22](https://github.com/unjs/hookable/commit/c2e1e223d16e7bf87117cd8d72ad3ba211a333d8))**
  160. - We no longer provide an IE11 compatible umd build. Instead, you should use an ESM-aware bundler such as webpack or rollup to transpile if needed.
  161. - Logger param is dropped. We use `console.warn` by default for deprecated hooks.
  162. - Package now uses named exports. You should import `{ Hookable }` instead of `Hookable` or use new `createHooks` util
  163. - `mergeHooks` util is exported standalone. You should replace `Hookable.mergeHooks` and `this.mergeHooks` with new `{ mergeHooks }` export
  164. - In versions < 5.0.0 when using `callHook` if an error happened by one of the hook callbacks, we was handling errors globally and call global `error` hook + `console.error` instead and resolve `callHook` promise! This sometimes makes confusing behavior when we think code worked but it didn't. v5 introduced a breaking change that when a hook throws an error, `callHook` also rejects instead of a global `error` event. This means you should be careful to handle all errors when using `callHook` now.
  165. ## Credits
  166. Extracted from [Nuxt](https://github.com/nuxt/nuxt.js) hooks system originally introduced by [Sébastien Chopin](https://github.com/Atinux)
  167. Thanks to [Joe Paice](https://github.com/RGBboy) for donating [hookable](https://www.npmjs.com/package/hookable) package name.
  168. ## License
  169. MIT - Made with 💖
  170. <!-- Badges -->
  171. [npm-version-src]: https://img.shields.io/npm/v/hookable?style=flat&colorA=18181B&colorB=F0DB4F
  172. [npm-version-href]: https://npmjs.com/package/hookable
  173. [npm-downloads-src]: https://img.shields.io/npm/dm/hookable?style=flat&colorA=18181B&colorB=F0DB4F
  174. [npm-downloads-href]: https://npmjs.com/package/hookable
  175. [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/hookable/main?style=flat&colorA=18181B&colorB=F0DB4F
  176. [codecov-href]: https://codecov.io/gh/unjs/h3
  177. [bundle-src]: https://img.shields.io/bundlephobia/minzip/hookable?style=flat&colorA=18181B&colorB=F0DB4F
  178. [bundle-href]: https://bundlephobia.com/result?p=hookable
  179. [license-src]: https://img.shields.io/github/license/unjs/hookable.svg?style=flat&colorA=18181B&colorB=F0DB4F
  180. [license-href]: https://github.com/unjs/hookable/blob/main/LICENSE