React 手写render方法,将虚拟DOM转为真实DOM

render方法的源码,将虚拟DOM转为真实DOM

函数式组件

特点

  1. 函数组件的名称首字母大写
  2. 函数式组件的返回值 => jsx
  3. jsx是一个父子组件
  4. 具有props属性

定义并使用函数式组件

function FunctionComponent (props){
	return <div>{props.name}</div>
}

const element = <FunctionComponent name={'100'}></FunctionComponent

babel转译后的代码

"use strict";

function FunctionComponent(props) {
  return /*#__PURE__*/React.createElement("div", null, props.name);
}

const element = /*#__PURE__*/React.createElement(FunctionComponent, {
  name: '100'
});

打印element

$$typeof: Symbol(react.element)
key: null
props: {name: '100'}
ref: null
type: ƒ FunctionComponent(props)
_owner: null
_store: {validated: false}
_self: null
_source: null

发现

我们可以看到elementtype不再是div等标签,而是一个函数

获取在父组件下的完整虚拟dom

function mountFunctionComponent (vdom) {
    // type为一个函数
    let {type,props} = vdom
    // 将子组件的的props传入到父组件中,得到一个完整的虚拟dom
    let functionVdom = type(props)
    return functionVdom
}

类组件

区别类组件和函数组件

vdom.type.prototype.isReactComponent

完整代码

/**
 * children为文本 { type: 'div', props: { className: 'title', style: { color: '#f69' }, children: 'one node' }, };
 * children为一个元素 { type: 'div', props: { children: { type: 'span', props: { children: 'one child node' }, }, }, }
 */


/**
 * 初始化react元素
 * @param {*} vdom 虚拟DOM 
 * @param {*} container 位置(容器)
 */
function render(vdom, container) {
    // 本质就是挂载,因container本身存在于文档之中,所以挂载操作会触发渲染
    mount(vdom, container)
}

// 将第一个参数变成真实DOM,插入到第二个参数挂载元素上
function mount(vdom, container) {
    const DOM = createDOM(vdom);
    container.append(DOM);
}

// 创建真实DOM
function createDOM(vdom) {
    // 是否为 文本 或 无内容
    if(typeof vdom === 'string' || typeof vdom === null){
        return document.createTextNode(vdom || '')
    } else if (typeof vdom.type === 'function'){
        if(vdom.type.prototype.isReactComponent){
            return mountClassComponent(vdom)
        }
        return mountFunctionComponent(vdom)
    }else {
        return createElementDOM(vdom)
    }


    function createElementDOM(vdom) {
        const { type, props } = vdom;
        let DOM = document.createElement(type); // 创建节点
        // 处理props
        if (props) {
            updateProps(DOM, props);
            const { children } = props;
            children && updateChildren(DOM, children);
        }
        return DOM;
    }
}

// 处理函数式组件
function mountFunctionComponent(vdom) {
    // type为一个函数
    let {type,props} = vdom
    // 将子组件的的props传入到父组件中,得到一个完整的虚拟dom
    let functionVdom = type(props)

    return createDOM(functionVdom)
}

// 处理类组件
function mountClassComponent(vdom) {
    let {type,props} = vdom
    // 注意 type 是一个类
    let classInstance = new type(props)
    // 得到虚拟dom
    let classVnode = classInstance.render()
    return createDOM(classVnode)
}

// 将属性(除了children)放置标签(DOM)中
function updateProps(DOM, props) {
    for (const key in props) {
        if (key === 'children') continue;
        if (key === 'style') {
            // 添加样式
            updateStyle(DOM, props[key]);
            continue;
        }
        // 添加其他属性,如ref和自定义属性
        DOM[key] = props[key];
    }
    function updateStyle(DOM, styleObj) {
        for (const key in styleObj) {
            DOM.style[key] = styleObj[key];
        }
    }
}

function updateChildren(DOM, children) {
    // 单个节点,直接插入(挂载)到DOM上; 多个节点,遍历插入
    const isOneChildren = !Array.isArray(children);
    isOneChildren
        ? mount(children, DOM)
        : children.forEach((child) => mount(child, DOM));
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值