前置知识jsx本质链接:https://www.yuque.com/linhe-8mnf5/fxyxkm/xekk7b
1、React渲染过程

- 把虚拟DOM变为真实DOM;
- 把虚拟DOM属性同步到真实DOM上;
- 把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;
- 把最终DOM挂载到容器上;
2、实现步骤一:将子节点挂载到根节点上

2.1 初始化项目文件

2.2 index内容初始化
import React from './react'; // 注意:使用的是自己写的reactimport ReactDOM from './react-dom'; // 注意:使用的是自己写的react-domconst element1 = (<div className="title" style={{ color: 'red' }}><span>hello word</span></div>)ReactDOM.render(element1,document.getElementById('root'));
2.3 实现react.js文件内容
/*** 实现createElement* @param {*} type 元素类型* @param {*} config 配对对象* @param {*} children 子元素*/function createElement(type, config, children) {if (config) {delete config.__source;delete config.__self;}let props = { ...config }if (arguments?.length > 3) {children = [].slice.call(arguments, 2)}props.children = childrenreturn {type,props,children}}const React = { createElement }export default React
2.4 实现react-dom.js文件内容
/*** 1.把虚拟DOM变为真实DOM;* 2.把虚拟DOM属性同步到真实DOM上;* 3.把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;* 4.把最终DOM挂载到容器上;* @param {*} vdom 要渲染的虚拟DOM* @param {*} container 虚拟DOM转换成真实DOM,并插入到容器中*/function render(vdom, container) {console.log(`vdom`, vdom)const dom = createDom(vdom)container.appendChild(dom)}/*** 虚拟dom变为真实dom* @param {*} vdom 虚拟dom* {"type":"div","key":null,"ref":null,* "props":{"className":"title","style":{"color":"red"},* "children":{"type":"span","key":null,* "ref":null,"props":{"children":"hello word"}*/function createDom(vdom) {// 如果是字符串或者数字,直接返回真实节点if (['number', 'string'].includes(typeof vdom)) {console.log(`vdom`, vdom)return document.createTextNode(vdom)}// 否则它就是一个react元素了const { type, props } = vdomlet dom = document.createElement(type)return dom}const ReactDom = { render }export default ReactDom
2.5 实现效果

3、实现步骤二:将虚拟dom的属性更新到真实dom
3.1 加入处理属性函数


实现代码:
// react-dom文件function createDom(vdom) {// 如果是字符串或者数字,直接返回真实节点if (['number', 'string'].includes(typeof vdom)) {return document.createTextNode(vdom)}// 否则它就是一个react元素了const { type, props } = vdomlet dom = document.createElement(type)// 将虚拟dom属性更新到真实dom上updateProps(dom, props)return dom}/*** 将虚拟dom属性更新到真实dom上* @param {*} dom 真实dom* @param {*} newProps 新属性*/function updateProps(dom, newProps) {for (let key in newProps) {if (key === 'children') continue// style单独处理,因为dom不支持if (key === 'style') {const styleObj = newProps.stylefor (let attr in styleObj) {dom.style[attr] = styleObj[attr]}}else{dom[key] = newProps[key]}}}
3.2 实现效果

4 实现步骤三:将子节点dom挂载到当前节点

4.1 处理子节点文本节点

4.2 处理子节是虚拟dom

4.3 处理子节点为数组


4.4 不满足以上条件处理

4.5 实现效果

5、React-dom完整代码实现
/*** createElement编译出来的结果* {"type":"div","key":null,"ref":null,* "props":{"className":"title","style":{"color":"red"},* "children":{"type":"span","key":null,* "ref":null,"props":{"children":"hello word"}*//*** 1.把虚拟DOM变为真实DOM;* 2.把虚拟DOM属性同步到真实DOM上;* 3.把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;* 4.把最终DOM挂载到容器上;* @param {*} vdom 要渲染的虚拟DOM* @param {*} container 虚拟DOM转换成真实DOM,并插入到容器中*/function render(vdom, container) {const dom = createDom(vdom)container.appendChild(dom)}/*** 虚拟dom变为真实dom* @param {*} vdom 虚拟dom*/function createDom(vdom) {// 如果是字符串或者数字,直接返回真实节点if (['number', 'string'].includes(typeof vdom)) {return document.createTextNode(vdom)}// 否则它就是一个react元素了const { type, props } = vdomconst dom = document.createElement(type)// 将虚拟dom属性更新到真实dom上updateProps(dom, props)// 如果儿子只有一个儿子,并且儿子是文本节点if (['number', 'string'].includes(typeof props.children)) {dom.textContent = props.children// 如果儿子是一个对象,并且是一个虚拟dom,递归调用render} else if (typeof props.children === 'object' && props.children.type) {// 把儿子变成真实dom,插入到自己身上render(props.children, dom)// 如果儿子是一个数组,说明儿子不止一个} else if (Array.isArray) {reconcileChildren(props.children, dom)} else {document.textContent = props.children ? props.children.toString() : ""}// // 把真实dom作为一个虚拟属性放在虚拟dom上,为以后更新做准备// vdom.dom = domreturn dom}/*** @param {*} childrenVdom 儿子们的虚拟dom* @param {*} parentDom 父的真实dom*/function reconcileChildren(childrenVdom, parentDom) {for (const childVdom of childrenVdom) {render(childVdom, parentDom)}}/*** 将虚拟dom属性更新到真实dom上* @param {*} dom 真实dom* @param {*} newProps 新属性*/function updateProps(dom, newProps) {for (const key in newProps) {if (key === 'children') continue// style单独处理,因为dom不支持if (key === 'style') {const styleObj = newProps.stylefor (let attr in styleObj) {dom.style[attr] = styleObj[attr]}} else {dom[key] = newProps[key]}}}const ReactDom = { render }export default ReactDom
6、源代码
本文代码:https://gitee.com/linhexs/react-write/tree/1.react-render/
