createElement然后遍历children属性,完成整个dom的创建,其实是相当耗费性能的,react之所以能实现高性能,是因为它实现了局部dom的更新,而不是像我们_render方法里面那么简单粗暴。 更新优化算法diff
ReactDOM.render(Hello, world!
, document.getElementById('root') );
实际上是JSX进过转换是这样的
ReactDOM.render(
React.createElement( 'h1', null, 'Hello, world!' ),
document.getElementById('root')
);
render方法的作用就是将对象转换成虚拟DOM,再最终渲染成真实的DOM
function render( vnode, container ) {
// 当vnode为字符串时,渲染结果是一段文本 // 1. 判断节点类型
if ( typeof vnode === 'string' ) {
const textNode = document.createTextNode( vnode );
return container.appendChild( textNode ); // 2. 给真实节点返回该类型的节点
}
const dom = document.createElement( vnode.tag ); // 3. 获取要创建节点的标签名
if ( vnode.attrs ) { // 4. 判断是否有attr属性,有则遍历attr属性给每个设置属性
Object.keys( vnode.attrs ).forEach( key => {
const value = vnode.attrs[ key ];
setAttribute( dom, key, value ); // 设置属性
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 5. 递归渲染子节点
return container.appendChild( dom ); // 6. 将渲染结果挂载到真正的DOM上
}
还有setAttribute方法:
function setAttribute( dom, name, value ) {
// 如果属性名是className,则改回class
if ( name === 'className' ) name = 'class';
// 如果属性名是onXXX,则是一个事件监听方法
if ( /on\w+/.test( name ) ) {
name = name.toLowerCase();
dom[ name ] = value || '';
// 如果属性名是style,则更新style对象
} else if ( name === 'style' ) {
if ( !value || typeof value === 'string' ) {
dom.style.cssText = value || '';
} else if ( value && typeof value === 'object' ) {
for ( let name in value ) {
// 可以通过style={ width: 20 }这种形式来设置样式,可以省略掉单位px
dom.style[ name ] = typeof value[ name ] === 'number' ? value[ name ] + 'px' : value[ name ];
}
}
// 普通属性则直接更新属性
} else {
if ( name in dom ) {
dom[ name ] = value || '';
}
if ( value ) {
dom.setAttribute( name, value );
} else {
dom.removeAttribute( name );
}
}
}
每次render前,清理一下之前的内容:
const ReactDOM = {
render: ( vnode, container ) => {
container.innerHTML = '';
return render( vnode, container );
}
}
在index.html里面增加容器: