January 02, 2018
从基本的preact.js看起。
Preact使用h()
来把JSX转换为虚拟DOM elements。
import { VNode } from './vnode'; // VNode构造,是一个空构造函数
import options from './options'; // options对dom,vdom转换的一些配置项,初始为空
const stack = [];
const EMPTY_CHILDREN = [];
export function h(nodeName, attributes) {
// nodeName可以认为是type,对应的是span,div等
// attributes对应dom的attributes,例如id,className(class)
let children=EMPTY_CHILDREN, lastSimple, child, simple, i;
// 构建JSX的参数为(type, attributes, ...children),这里把children都push到stack中
for (i=arguments.length; i-- > 2; ) {
stack.push(arguments[i]);
}
// 判定attribute是否存在,同样将children push到stack中,完毕后删除
if (attributes && attributes.children!=null) {
if (!stack.length) stack.push(attributes.children);
delete attributes.children;
}
// 执行迭代
while (stack.length) {
// 如果是数组,把child从children数组中单独push,相当于对嵌套子节点做了一次flatten
if ((child = stack.pop()) && child.pop!==undefined) {
for (i=child.length; i--; ) stack.push(child[i]);
}
// 对数组类型做区分
else {
// boolean型转为null
if (typeof child==='boolean') child = null;
// 如果不是函数,可能是其他类型
// 把null,undefined转为''
// 把number转为String(number)
// Symbol, Object则将simple置为false
if ((simple = typeof nodeName!=='function')) {
if (child==null) child = '';
else if (typeof child==='number') child = String(child);
else if (typeof child!=='string') simple = false;
// 注意这里,在对这些类型判断完毕以后,如果simple === false,则有两种可能
// 1.simple是function
// 2.simple是Symbol或者Object
}
// 分情况判断
// 1. 首次执行,则lastSimple肯定为undefined,且children===EMPTY_CHILDREN成立
// 2. 第二次执行,主要区别是下面的判定,可以总结为simple和lastSimple都为true
// 满足这个条件的,就是上一次和这一次的child为string、null、boolean、number、undefined即可
if (simple && lastSimple) {
// 上面会把这些类型转为字符串,这里使用简单的+=就将字符串拼接到一起
// 减少了children的数量,有助于提高性能
children[children.length-1] += child;
}
else if (children===EMPTY_CHILDREN) {
// 首次执行后,这里切换了children指向,之后这个条件将不会成立
children = [child];
}
else {
children.push(child);
}
lastSimple = simple;
}
}
// VNode暂时还没有做什么事情,单纯记录一些信息
let p = new VNode();
p.nodeName = nodeName;
p.children = children;
p.attributes = attributes==null ? undefined : attributes;
p.key = attributes==null ? undefined : attributes.key;
// if a "vnode hook" is defined, pass every created VNode to it
// 如果在options里定义了vnode hook,就执行
if (options.vnode!==undefined) options.vnode(p);
return p;
}
需要明确,h()
不负责JSX语法转为JavaScript对象,通常这件事是由babel去做,h()
负责把JSX生成的JavaScript对象处理为VNode。
也有一个htm库,可以在浏览器端将JSX转为JavaScript对象,然后再render为html。
记录一些东西。 By liuyj