createElement 是干嘛的?
用于创建虚拟DOM,react的DOM渲染方式便是通过虚拟DOM转换为真实的DOM最后渲染到页面上去
React 渲染的大致流程
DOM元素写在js中就是jsx
React在通过webpack打包之后,会通过babel生成一个 React.createElement() 方法
这个方法将jsx接收进去,最后返回一个vnode,也就是虚拟DOM
最后将转换好的vnode通过react-dom的render方法转换为真实dom,挂载到指定的元素上去
createElement 方法实现
在实现之前,我们先来看一下react转换过后虚拟dom的大致结构
{
$$typeof: Symbol(react.element),
key: null,
props: {
children: "hello world",
className: "title",
style: {color: '#fff'},
},
type: "h3",
ref: null,
}
-
其中 $$typeof 是一个标识符,用来表示你创建的是一个element元素还是一个文本 -
key 和 ref 是直接添加到dom上的属性,分别用于后续的 diff算法和读取真实DOM的操作 -
type 就是你要创建的标签 -
props 中存放的就是你标签的内容 children, 类名className和样式style下面开始正式实现createElement方法
在react中,此方法会接收三个参数 1.type 2.config 3.children 第一个参数是标签的类型 第二个参数是类名、样式、key等属性配置 第三个参数就表示的是你要渲染的值这里第三个参数中children比较特殊,他可以是多个,也可以是一个或者没有。当他是一个的时候就会是一个字符串,多个时会转换为一个数组,并且children可以接收转换过后的虚拟DOM对象
// 大概类似这样 React.createElement("h3", { className: "title", style: { color: "#fff" } }, "hello world", React.createElement("span", null, "hello"))1. 首先我们来定义一个函数
function createElement(type, config, children) { // 返回值为一个虚拟DOM对象 return { type, } }2. 然后我们处理较为复杂的props字段
function createElement(type, config, children) { // config中的样式类名等属性是我们需要的,所以直接展开 let props = {...config} if(config) { /* 这里children的值可以是多个,所以进行多次判断 */ if(arguments.length > 3) { /* 这里可以使用更简单的写法,就是多个值的话截取参数的第三个往后,最后返回一个数组 使用 Array.from() 把arguments转换成真实数组,然后再进行截取就可以了 */ props.children = Array.prototype.slice.call(arguments, 2) } else if (arguments.length === 3) { // children 只有一个值直接赋值就行,没值的话可以忽略不计 props.children = children } } return { type, props, // 最后把props返回出来 } }3. Key和Ref其实是定义在第一层的,但是是通过config参数接收的,所有我们要去单独处理他
function createElement(type, config, children) { // 处理 key ref let key,ref if(config) { key = config.key ref = config.ref // 这里要将config中的key和ref删除 // 防止下面处理children时将key和ref添加到props中 delete config.key delete config.ref } // config中的样式类名等属性是我们需要的,所以直接展开 let props = {...config} if(config) { // 这里children的值可以是多个,所以进行多次判断 if(arguments.length > 3) { props.children = Array.prototype.slice.call(arguments, 2) } else if (arguments.length === 3) { props.children = children } } return { // $$typeofs 这里这个属性用来区别你创建的是element还是文本 // 文本类型后面虚拟dom转换为真实dom时会使用到 $$typeofs: Symbol("react.element"), type, props, key, // 把这两个属性添加上去即可 ref, } }4. 最后就是调用我们封装好的createElement方法
let element = createElement("h3", { className: "title", style: { color: "#fff" } }, "hello world", React.createElement("span", null, "hello"))这里只是对createElement函数进行了一次简单的封装,在后续实现render()方法中会将其进一步完善