React虚拟DOM转换为真实DOM

在上一篇博客React虚拟DOM原理中,我们探索了如何创建虚拟DOM,那么创建了虚拟DOM之后,又是怎么转换成真实DOM,渲染在页面中的呢,下面我们继续探索。

在React项目的入口处,经常有ReactDOM.render(<App />, document.getElementById('root'))这样的写法,那么这是不是将所有的虚拟DOM转换成真实DOM插入到root中的操作呢。

render方法

按照上面的猜想,那么render方法的作用就是

  • 虚拟DOM转换成了真实DOM
  • 真实DOM插入到root

render方法接收两个参数,第二个参数没什么好说的, 固定写法,第一个参数有以下几种情况:

  1. 类组件
  2. 函数组件
  3. 文字、数字

首先处理类组件,我们先回忆一下类组件的写法,我们想要获得的DOM在render方法中,那么使用new App().render()方法不就能得到DOM了吗。

class App extends Component {
    render() {
        return (
            <div>
                App
            </div>
        )
    }
}

然后再来处理函数组件,函数组件的写法是,直接就返回了DOM

function App() {
    return <div>App</div>
}

对于文字、数字,直接添加到根节点。

这样就处理了各种组件类型,还有个问题就是这些组件的属性怎么处理。

  1. 属性是className,直接添加到标签上
  2. 属性是style,将样式添加到标签上
  3. 属性是children,如果是文字、数字,直接插入节点,如果是DOM元素,则循环递归处理
  4. 其他情况,当做普通属性直接setAttribute

src下创建react-dom.js,代码如下

function render(element, parentNode) {
    // 如果是字符串或者数字,直接添加到根节点
    if(typeof element === 'string ' || typeof element === 'number') {
        return parentNode.appendChild(document.createTextNode(element));
    }
    let type, props;
    type = element.type;
    props = element.props;
    if(type && type.isReactComponent) {
        // 如果是类组件,拿到render方法中的内容,也就是React.createElement()的返回值
        let returnElement = new type(props).render();
        type = returnElement.type;
        props = returnElement.props;
    } else if(typeof type === 'function') {
        // 如果是函数组件,直接拿到React.createElement()的返回值
        let returnElement = type(props);
        type = returnElement.type;
        props = returnElement.props;
    }

    let domElement = document.createElement(type);  // 创建一个标签<div></div>、<span></span>之类的
    for(let propName in props) {
        if(propName === 'className') {
            // 如果属性是className,不做处理,直接加到标签上
            domElement.className = props[propName];
        } else if(propName === 'style') {
            // 如果是style,直接设置样式
            let styleObj = props[propName];
            domElement.style.cssText = styleObj;
        } else if(propName === 'children') {
            console.log(props[propName])
            // 如果是children
            if(typeof props[propName] === 'string' || typeof props[propName] === 'number') {
                // 如果内容是文字、数字之类的,把文字、数字放到标签里面
                domElement.appendChild(document.createTextNode(props[propName]));
            } else {
                // 如果里面还有标签,先循环再递归
                props.children.forEach(child => render(child, domElement));
            }
        } else {
            // 其余情况,当做普通属性处理
            domElement.setAttribute(propName,props[propName]);
        }
    }
    parentNode.appendChild(domElement);  // 将所有标签处理完毕后插入跟节点
}

export default { render };

这样就实现了一个简单的render方法,当然还有很多情况需要做兼容,有兴趣的读者可以自行尝试。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值