Skip to content

08.实现函数setPropsForDOM进行属性更新

上一小节我们已经将一个字符串渲染到了页面上,但这个字符串没有样式,假设我们在index.js中的jsx给div添加一些属性,会发现页面内容没有任何样式上的区别。这是因为我们根据虚拟DOM创建DOM的时候,也就是createDOM函数中,没有对属性进行处理,这一小节就来完成这项工作,将虚拟DOM上携带的属性信息追加到创建的真实DOM上。具体代码变化过程请观看视频,这里把关键代码展示出来:

index.js:

js
import React from './react';
import ReactDOM from './react-dom';
// 【这里需要注意jsx属性名称的写法】
ReactDOM.render(<div className='test-class' style={{color: 'red'}}>Simple React App<span>xx1</span><span>xx2</span></div>, document.getElementById('root'))

react-dom.js:

js
function createDOM(VNode){
    const {type, props} = VNode
    let dom;
    if (type && VNode.$$typeof === REACT_ELEMENT) {
        dom = document.createElement(type);
    }
    if(props){
        if (typeof props.children === 'object' && props.children.type) {
            mount(props.children, dom)
        } else if (Array.isArray(props.children)) {
            mountArray(props.children, dom);
        } else if (typeof props.children === 'string'){
            dom.appendChild(document.createTextNode(props.children));
        }
    }
    setPropsForDOM(dom, props) // 【增加的逻辑】
    return dom
}

// 主要不要去死记硬背函数名称,我们这个版本的函数名称和原版源码的名称有所差异,但思路是一致的
function setPropsForDOM(dom, VNodeProps = {}) {
    if(!dom) return
    for (let key in VNodeProps) {
        if (key === 'children') continue;
        if (/^on[A-Z].*/.test(key)) { // 大家需要注意这个正则表达式为什么这么写 
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
            // ^ :开头
            // . :含义1: Matches any single character except line terminators: \n, \r, \u2028分隔符 or \u2029段落分隔符. For example, /.y/ matches "my" and "ay", but not "yes", in "yes make my day", as there is no character before "y" in "yes".
            // . :含义2: Inside a character class, the dot loses its special meaning and matches a literal dot.
            // * : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Quantifiers Matches the preceding item "x" 0 or more times.
            // TODO: 事件相关内容我们在后续单独介绍
        } else if (key === 'style') {
            Object.keys(VNodeProps[key]).forEach(styleName => {
                dom.style[styleName] = (VNodeProps[key])[styleName];
            })
        } else {
            // 如果用函数setAttribute(key, VNodeProps[key]),则需要对key值进行转化
            // dom上的属性名称和jsx的属性名称基本一致,但和我们编写html时候的属性名称是有差异的,需要注意
            // 在官方文档上有关于属性名称的说明:https://reactjs.org/docs/introducing-jsx.html
            // Since JSX is closer to JavaScript than to HTML, React DOM uses camelCase property naming convention instead of HTML attribute names.
            // For example, class becomes className in JSX, and tabindex becomes tabIndex.
            dom[key] = VNodeProps[key]
        }
    }
}

基于 VitePress 构建