snabbdom (四)
大约 2 分钟
htmldomapi.ts
这个文件里面的是 dom 操作的 api
export interface DOMAPI {
createElement: (tagName: any) => HTMLElement;
createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
createTextNode: (text: string) => Text;
createComment: (text: string) => Comment;
insertBefore: (
parentNode: Node,
newNode: Node,
referenceNode: Node | null,
) => void;
removeChild: (node: Node, child: Node) => void;
appendChild: (node: Node, child: Node) => void;
parentNode: (node: Node) => Node | null;
nextSibling: (node: Node) => Node | null;
tagName: (elm: Element) => string;
setTextContent: (node: Node, text: string | null) => void;
getTextContent: (node: Node) => string | null;
isElement: (node: Node) => node is Element;
isText: (node: Node) => node is Text;
isComment: (node: Node) => node is Comment;
}
// 创建一个元素
function createElement(tagName: any): HTMLElement {
return document.createElement(tagName);
}
// 创建一个含有命名空间的元素
function createElementNS(namespaceURI: string, qualifiedName: string): Element {
return document.createElementNS(namespaceURI, qualifiedName);
}
// 创建一个text节点
function createTextNode(text: string): Text {
return document.createTextNode(text);
}
// 创建一个注释节点
function createComment(text: string): Comment {
return document.createComment(text);
}
// 插入在指定节点前面
function insertBefore(
parentNode: Node,
newNode: Node,
referenceNode: Node | null,
): void {
parentNode.insertBefore(newNode, referenceNode);
}
// 移出节点
function removeChild(node: Node, child: Node): void {
node.removeChild(child);
}
// 添加节点
function appendChild(node: Node, child: Node): void {
node.appendChild(child);
}
// 获取父亲节点
function parentNode(node: Node): Node | null {
return node.parentNode;
}
// 获取下一个兄弟节点
function nextSibling(node: Node): Node | null {
return node.nextSibling;
}
// 获取标签名
function tagName(elm: Element): string {
return elm.tagName;
}
// 设置textContent
function setTextContent(node: Node, text: string | null): void {
node.textContent = text;
}
// 获取textContext
function getTextContent(node: Node): string | null {
return node.textContent;
}
// 判断是不是元素节点
function isElement(node: Node): node is Element {
return node.nodeType === 1;
}
// 判断是不是文本节点
function isText(node: Node): node is Text {
return node.nodeType === 3;
}
// 判断是不是注释节点
function isComment(node: Node): node is Comment {
return node.nodeType === 8;
}
export const htmlDomApi: DOMAPI = {
createElement,
createElementNS,
createTextNode,
createComment,
insertBefore,
removeChild,
appendChild,
parentNode,
nextSibling,
tagName,
setTextContent,
getTextContent,
isElement,
isText,
isComment,
};
is.ts
两个工具方法
export const array = Array.isArray;
export function primitive(s: any): s is string | number {
return typeof s === 'string' || typeof s === 'number';
}
tovnode.ts
原生的 dom 转 vnode
import { vnode, VNode } from './vnode';
import { htmlDomApi, DOMAPI } from './htmldomapi';
export function toVNode(node: Node, domApi?: DOMAPI): VNode {
const api: DOMAPI = domApi !== undefined ? domApi : htmlDomApi;
let text: string;
if (api.isElement(node)) {
const id = node.id ? '#' + node.id : '';
const cn = node.getAttribute('class');
const c = cn ? '.' + cn.split(' ').join('.') : '';
const sel = api.tagName(node).toLowerCase() + id + c;
const attrs: any = {};
const children: VNode[] = [];
let name: string;
let i: number, n: number;
const elmAttrs = node.attributes;
const elmChildren = node.childNodes;
for (i = 0, n = elmAttrs.length; i < n; i++) {
name = elmAttrs[i].nodeName;
if (name !== 'id' && name !== 'class') {
attrs[name] = elmAttrs[i].nodeValue;
}
}
for (i = 0, n = elmChildren.length; i < n; i++) {
children.push(toVNode(elmChildren[i], domApi));
}
return vnode(sel, { attrs }, children, undefined, node);
} else if (api.isText(node)) {
text = api.getTextContent(node) as string;
return vnode(undefined, undefined, undefined, text, node);
} else if (api.isComment(node)) {
text = api.getTextContent(node) as string;
return vnode('!', {}, [], text, node as any);
} else {
return vnode('', {}, [], undefined, node as any);
}
}