Skip to content

jsx为原生dom的渲染 ​

本节目标:理解“jsx为原生dom的渲染”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 import React from

"./react";
import

ReactDOM from

"./react-dom";
//

babel会调用自动调用React.createElement去转换 let element1 = (

<div
className="title"
style={{

color: "red"

}}>
<span>hello
world</span>
</div>
);
debugger
//

了解元素element主要属性

console.log(element1);
ReactDOM.render('',
document.getElementById("root"));

先调用了reactDOM.render方法; 在这个方法中,我们传入了createElement返回的普通对象,也就是vdom;它的结构如下:

可见,对于children,如果只是单个,将是一个对象,如果是多个,将会是一个数组; 它本质上来说,就是用来描述jsx的一个对象,说到底,它描述的是一个会生成dom结构的函数,它描述了函数的名称type,它的参数,还是它返回的jsx结构的描述; let element1 = (

<div
className="title"
style={{

color: "red"

}}>
<span>hello
world</span>
<span>hello
javascript</span>
</div>
);

此外,如果children是基本数据类型,其中undefined,null,布尔值,不会渲染处理,而字符串和数字才会处理; let element1 = (

<div
className="title"
style={{

color: "red"

}}>
 `{null}`
`{false}`
`{undefined}`
`{true}`
<span>hello
javascript</span>
</div>
);

function

createElement(type,
config,
children)

{let

ref;//

可以通过 ref引用此元素 let

key;//

可以唯一标识一个子元素 if

(config)

{delete

config.__source;
     `delete`
config.__self;
     `ref` `=`
config.ref;
     `key` `=`
config.key;
     `delete`
config.ref;
     `delete`
config.key;
 `}`
`let` `props` `=` `{`
...config

};if

(arguments.length

\>

3)

{

props.children

=

Array.prototype.slice.call(arguments,
2).map(wrapToVdom);
 `}` `else` `{`
props.children

=

wrapToVdom(children);//children

可能是React元素对象,也可能是一个字符串 数字 null undefined}return {

type,
ref,
key,

props

};
}
//

转换文本或数字为特殊的

vdom
export

function

wrapToVdom(element)

{return typeof element === "string" || typeof element === "number"? { type:

REACT_TEXT,

props: { content: element } }:

element;
}

这里没有处理null,undefined,布尔值的情况;

然后我们在render中调用了mount方法: function

render(vdom,
container)

{

mount(vdom,
container);
}

在mount方法中,我们主要是:

  • 使用vdom创建一个真实dom;
  • 然后操作dom,把这个真实dom,挂载到容器中:

export function

mount(vdom,
container)

{let newDOM =

createDOM(vdom);
container.appendChild(newDOM);
}

我们创建真实dom要调用createDOM方法,这个方法比较重要:

  • 它会判断vdom的type,根据类型的不同,调用不出的函数来创建出真实dom;
  • 其中type有几个重要类型:
    • 一是字符串,这是原生dom;
    • 一是普通对象,这个对象上我们会加上$$typeof来分别出这些对象的区别,判断是使用type&&type.$$typeof;
    • 另外是函数类型,这是还是区分是类组件还是函数组件;

先创建要节点的dom,然后处理children,如果children是单个对象,就直接mount这个对象到父元素即可,也就是以这个对象创建新dom,然后挂载到父元素即可; 如果children是数组,也简单,循环这个数组,然后把每一个元素挂载到父元素上; 思考,能不能先创建所有好子dom,然后一起挂载; export function

createDOM(vdom)

{if

(!vdom)

return

null;

let {

type,

props } =

vdom;

let

dom;//

真实DOMif

(type

===

REACT_TEXT)
{//

如果这个元素是一个文本的话 dom =

document.createTextNode(props.content);

} else {dom =

document.createElement(type);//

div span p}//处理属性 if

(props)

{

updateProps(dom,

{},

props);
 `//` 处理`children`
`if`
(props.children)

{let children =

props.children;
   `if`
(typeof

children === 'object' &&

children.type)

{//说明这是一个React元素

mount(children,
dom);
   `}` `else` `if`
(Array.isArray(children))

{

reconcileChildren(props.children,
dom);
   `}`
`}`

}//让虚拟DOM的dom属性指向这个虚拟DOM对应的真实DOM

vdom.dom

=

dom;

return

dom;
}

function

updateProps(dom,
oldProps,
newProps)

{for

(let

key in

newProps)

{if

(key

===

"children")

{

continue;
 `}`
`//` 处理`style`
`if`
(key

===

"style")

{let style =

newProps[key];
   `for`
(let

attr in

style)

{

dom.style[attr]

=

style[attr];
   `}`
`}` `else` `{`
dom[key]

=

newProps[key];
 `}`
}
}

function

reconcileChildren(childrenVdom,
parentDOM)

{for

(let

i =

0;

i \<

childrenVdom.length;
i++)

{let childVdom =

childrenVdom[i];
mount(childVdom,
parentDOM);
}
}

本站总访问 — 次