此文章没什么难度
// 类型校验工具
function typeOf (obj, type) {
const testType = Object.prototype.toString.call(obj).split(' ')[1].split(']')[0].toLowerCase();
return type === testType;
}
/**
* 创建虚拟dom对象
*/
export function createElement (type, props, ...childs) {
let jsxObj = {
type,
props: {},
key: null,
ref: null
};
if (props) {
// => 处理key和ref
if (props.hasOwnProperty("key")) {
jsxObj.key = props.key;
delete props.key;
}
if (props.hasOwnProperty("ref")) {
jsxObj.ref = props.ref;
delete props.ref;
}
jsxObj.props = Object.assign(jsxObj.props, props);
}
// => childrens 处理
if (childs.length > 0) {
childs = childs.length === 1 ? childs[0] : childs;
jsxObj.props["children"] = childs;
}
return jsxObj;
}
/**
* 把虚拟dom转化为真实dom
*/
export function render (jsxObj, container, callback) {
let { type, props } = jsxObj;
// 1. 根据type创建一个dom(真实dom)
let elem = document.createElement(type);
// 1.1 根据props,中的属性依次给创建的元素进行设置
for (let key in props) {
// 1.2 关于某些特殊属性的处理:className/style/children
if (/^className|style|children$/.test(key)) {
if (!props.hasOwnProperty(key)) break;
if (key === 'className') elem.className = props.className;
if (key === 'style') {
let styObj = props.style;
for (let sKey in styObj) {
if (!styObj.hasOwnProperty(sKey)) break;
elem["style"][sKey] = styObj[sKey];
}
}
if (key === 'children') {
let val = props['children'],
childrenArr = Array.isArray(val) ? val : [val];
// 循环迭代所有的子节点:如果是字符产 直接插入到当前dom中,如果是一个新的dom对象,递归调用render,再次创建元素插入到当前dom
childrenArr.forEach(item => {
if (typeOf(item) === 'string') {
elem.appendChild(document.createTextNode(item));
return;
}
render(item, elem);
})
}
continue;
}
elem.setAttribute(key, props.key);
}
// 2. 把创建的对象添加到指定容器中
container.appendChild(elem);
typeOf(callback, 'function') && callback();
}