>[success] # Snabbdom -- h函数 1. `h` 用来来创建 `vnodes`,这个函数需要三个参数 * 第一个参数,一个字符串类型的 标签或选择器 参数类型 `string` * 第二个参数,一个数据对象(可选)参数类型 `VNodeData` * 第三个参数,一个子节点数组或字符串(可选)参数类型`VNodeChildren` **现在想生成dom** `<div id='container' class='cls' style='color:red'>我是例子<span>例子<span></div>` * 通过`h` 函数生成写法(这个例子中第二个参数类型就是VNodeData,第三个参数类型VNodeChildren) ~~~ let vnode = h('div', { style: { backgroundColor: 'red' }, on: { click: eventHandler } }, [ '我是文本' h('h1', 'Hello Snabbdom'), h('p', '这是p标签') ]) ~~~ 2. 具体 `h` 函数定义 `snabbdom` 通过 用 **ts 重载**说明了几种使用情况 ~~~ export function h(sel: string): VNode; // (1) export function h(sel: string, data: VNodeData): VNode; // (2) export function h(sel: string, children: VNodeChildren): VNode; // (3) export function h(sel: string, data: VNodeData, children: VNodeChildren): VNode; // (4) export function h(sel: any, b?: any, c?: any): VNode {.....} ~~~ 上面情依次表示 * **只创建了dom节点(1)**,节点中没有任何内容 `<div id="app"></div>` ~~~ import { init, eventListenersModule, styleModule, h } from 'snabbdom' // 2. 注册模块 let patch = init([styleModule, eventListenersModule]) // 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象) let vnode = h('div#app') let app = document.querySelector('#app') patch(app, vnode) ~~~ * **创建了dom节点,并创建dom中元素(2)**,节点中没有任何内容 `<div id="app" class="name" style="background-color: red;"></div> ` ~~~ import { init, eventListenersModule, classModule, styleModule, h, } from 'snabbdom' // 2. 注册模块 let patch = init([styleModule, eventListenersModule, classModule]) // 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象) let vnode = h('div#app', { style: { backgroundColor: 'red', }, class: { name: true, age: false, }, }) let app = document.querySelector('#app') patch(app, vnode) ~~~ * **创建dom并且创建里面的内容(3)** `VNodeChildren` 类型作为`VNodeChildElement | VNodeChildElement[]`其中 `VNodeChildren`类型定义: ~~~ export type VNodeChildElement = VNode | string | number | undefined | null; export type ArrayOrElement<T> = T | T[]; export type VNodeChildren = ArrayOrElement<VNodeChildElement> ~~~ `<div id="app"><span>子节点</span>12334</div>` ~~~ import { init, eventListenersModule, styleModule, h } from 'snabbdom' // 2. 注册模块 let patch = init([styleModule, eventListenersModule]) // 3. 使用 h() 函数的第二个参数传入模块需要的数据(对象) let vnode = h('div#app', [h('span', '子节点'), 1, 23, 34]) let app = document.querySelector('#app') patch(app, vnode) ~~~ * **创建一个有属性有内容的节点(4)** 演示省略 >[danger] ##### 源码部分 通过 h 函数将传入参数 转换成了 `VNode(虚拟节点)` ~~~ export function h(sel: any, b ? : any, c ? : any): VNode { var data: VNodeData = {}, children: any, text: any, i: number; // 处理参数,实现重载的机制 if (c !== undefined) { // 处理三个参数的情况 // sel、data、children/text if (b !== null) { data = b; } if (is.array(c)) { children = c; } // 如果 c 是字符串或者数字 else if (is.primitive(c)) { text = c; } // 如果 c 是 VNode else if (c && c.sel) { children = [c]; } } else if (b !== undefined && b !== null) { // 处理两个参数的情况 // 如果 b 是数组 if (is.array(b)) { children = b; } // 如果 b 是字符串或者数字 else if (is.primitive(b)) { text = b; } // 如果 b 是 VNode else if (b && b.sel) { children = [b]; } else { data = b; } } if (children !== undefined) { // 处理 children 中的原始值(string/number) for (i = 0; i < children.length; ++i) { // 如果 child 是 string/number,创建文本节点 // 因为你的文本节点是'我是文本'类似这种在数组中不是一个h函数的 // 表现形式因此需要被转换成一个h函数的表现形式 if (is.primitive(children[i])) children[i] = vnode(undefined, undefined, undefined, children[i], undefined); } } if ( sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g' && (sel.length === 3 || sel[3] === '.' || sel[3] === '#') ) { // 如果是 svg,添加命名空间 addNS(data, children, sel); } // 返回 VNode return vnode(sel, data, children, text, undefined); }; // 导出模块 export default h; ~~~