>[success] # init 函数 1. `init` 函数接收一个包含模块的数组并返回一个具有指定功能的 `patch` 函数(`patch`函数用来进行 **新老虚拟`dom`** 比较) 2. `init` 函数中大部分都是用来和`patch` 搭配的工具方法,抛开这部逻辑`init`通过收集**注册模块的钩子函数** (`class /props /attributes/dataset /style/eventlisteners` )并在特定位置触发对应模块的钩子 ![](https://img.kancloud.cn/99/21/9921831e73585784d79ae5e5eb8f2f9e_781x799.png) 注册模块,下面**eventListenersModule, styleModule** 模块中都内置了钩子,案例中也在对应Vnode 节点中增加了自己的钩子`init`钩子该钩子是在`vnode 被添加`时候触发 ~~~ import { init, eventListenersModule, styleModule, h } from 'snabbdom' // 2. 注册模块 let patch = init([styleModule, eventListenersModule]) // 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象) let vnode = h( 'div', { hook: { init() { console.log(1111111111) // 执行后控制台打印 }, }, }, [(h('span', '子节点'), 1, 23, 34)] ) let app = document.querySelector('#app') patch(app, vnode) ~~~ * 以class 模块代码内部注册两个钩子 ![](https://img.kancloud.cn/92/0f/920f62c4e6c12b6a183032f6f45dd4bd_1533x649.png) * `init`函数注册模块钩子部分的代码,将这些模块的钩子函数按照`hooks`提供的进行分类最终构建的 cbs 对象的形式 `cbs = { create: [fn1, fn2], update: [], ...}`,将模块中的钩子统一注册,注意这里注册的只是模块中的钩子并不是我们`自定义Vnod`中的钩子 ~~~ const hooks: (keyof Module)[] = ['create', 'update', 'remove', 'destroy', 'pre', 'post' ]; export function init(modules: Array < Partial < Module >> , domApi ? : DOMAPI) { let i: number, j: number, cbs = ({} as ModuleHooks); // 初始化转换虚拟节点的 api const api: DOMAPI = domApi !== undefined ? domApi : htmlDomApi; // 把传入的所有模块的钩子方法,统一存储到 cbs 对象中 // 最终构建的 cbs 对象的形式 cbs = [ create: [fn1, fn2], update: [], ... ] for (i = 0; i < hooks.length; ++i) { // cbs['create'] = [] cbs[hooks[i]] = []; for (j = 0; j < modules.length; ++j) { // const hook = modules[0]['create'] const hook = modules[j][hooks[i]]; if (hook !== undefined) { (cbs[hooks[i]] as Array < any > ).push(hook); } } } ……………… return function patch(oldVnode: VNode | Element, vnode: VNode): VNode {} } ~~~ 3. `init` 的代码片段,采用了闭包的方式,两次调用patch,他们的配置都是**init 传入的参数**,因为**闭包延长了参数的作用域链**可以让后来的 **patch 都共享init**传入的配置参数,这样每次`patch` 调用的都是最初的`init`初始模块 ~~~ const patch = init(['a', 'b', 'c']) patch() // [ 'a', 'b', 'c' ] patch() // [ 'a', 'b', 'c' ] ~~~