前置知识jsx本质链接:https://www.yuque.com/linhe-8mnf5/fxyxkm/xekk7b

1、React渲染过程

image.png

  1. 把虚拟DOM变为真实DOM;
  2. 把虚拟DOM属性同步到真实DOM上;
  3. 把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;
  4. 把最终DOM挂载到容器上;

2、实现步骤一:将子节点挂载到根节点上

image.png

2.1 初始化项目文件

image.png

2.2 index内容初始化

  1. import React from './react'; // 注意:使用的是自己写的react
  2. import ReactDOM from './react-dom'; // 注意:使用的是自己写的react-dom
  3. const element1 = (<div className="title" style={{ color: 'red' }}><span>hello word</span></div>)
  4. ReactDOM.render(element1,
  5. document.getElementById('root')
  6. );

2.3 实现react.js文件内容

  1. /**
  2. * 实现createElement
  3. * @param {*} type 元素类型
  4. * @param {*} config 配对对象
  5. * @param {*} children 子元素
  6. */
  7. function createElement(type, config, children) {
  8. if (config) {
  9. delete config.__source;
  10. delete config.__self;
  11. }
  12. let props = { ...config }
  13. if (arguments?.length > 3) {
  14. children = [].slice.call(arguments, 2)
  15. }
  16. props.children = children
  17. return {
  18. type,
  19. props,
  20. children
  21. }
  22. }
  23. const React = { createElement }
  24. export default React

2.4 实现react-dom.js文件内容

  1. /**
  2. * 1.把虚拟DOM变为真实DOM;
  3. * 2.把虚拟DOM属性同步到真实DOM上;
  4. * 3.把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;
  5. * 4.把最终DOM挂载到容器上;
  6. * @param {*} vdom 要渲染的虚拟DOM
  7. * @param {*} container 虚拟DOM转换成真实DOM,并插入到容器中
  8. */
  9. function render(vdom, container) {
  10. console.log(`vdom`, vdom)
  11. const dom = createDom(vdom)
  12. container.appendChild(dom)
  13. }
  14. /**
  15. * 虚拟dom变为真实dom
  16. * @param {*} vdom 虚拟dom
  17. * {"type":"div","key":null,"ref":null,
  18. * "props":{"className":"title","style":{"color":"red"},
  19. * "children":{"type":"span","key":null,
  20. * "ref":null,"props":{"children":"hello word"}
  21. */
  22. function createDom(vdom) {
  23. // 如果是字符串或者数字,直接返回真实节点
  24. if (['number', 'string'].includes(typeof vdom)) {
  25. console.log(`vdom`, vdom)
  26. return document.createTextNode(vdom)
  27. }
  28. // 否则它就是一个react元素了
  29. const { type, props } = vdom
  30. let dom = document.createElement(type)
  31. return dom
  32. }
  33. const ReactDom = { render }
  34. export default ReactDom

2.5 实现效果

image.png

3、实现步骤二:将虚拟dom的属性更新到真实dom

image.png

3.1 加入处理属性函数

image.png

image.png

实现代码:

  1. // react-dom文件
  2. function createDom(vdom) {
  3. // 如果是字符串或者数字,直接返回真实节点
  4. if (['number', 'string'].includes(typeof vdom)) {
  5. return document.createTextNode(vdom)
  6. }
  7. // 否则它就是一个react元素了
  8. const { type, props } = vdom
  9. let dom = document.createElement(type)
  10. // 将虚拟dom属性更新到真实dom上
  11. updateProps(dom, props)
  12. return dom
  13. }
  14. /**
  15. * 将虚拟dom属性更新到真实dom上
  16. * @param {*} dom 真实dom
  17. * @param {*} newProps 新属性
  18. */
  19. function updateProps(dom, newProps) {
  20. for (let key in newProps) {
  21. if (key === 'children') continue
  22. // style单独处理,因为dom不支持
  23. if (key === 'style') {
  24. const styleObj = newProps.style
  25. for (let attr in styleObj) {
  26. dom.style[attr] = styleObj[attr]
  27. }
  28. }else{
  29. dom[key] = newProps[key]
  30. }
  31. }
  32. }

3.2 实现效果

image.png

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

image.png

4.1 处理子节点文本节点

image.png

4.2 处理子节是虚拟dom

image.png

4.3 处理子节点为数组

image.png
image.png

4.4 不满足以上条件处理

image.png

4.5 实现效果

image.png

5、React-dom完整代码实现

  1. /**
  2. * createElement编译出来的结果
  3. * {"type":"div","key":null,"ref":null,
  4. * "props":{"className":"title","style":{"color":"red"},
  5. * "children":{"type":"span","key":null,
  6. * "ref":null,"props":{"children":"hello word"}
  7. */
  8. /**
  9. * 1.把虚拟DOM变为真实DOM;
  10. * 2.把虚拟DOM属性同步到真实DOM上;
  11. * 3.把虚拟DOM上的children也变成真实DOM挂载到自己的DOM上;
  12. * 4.把最终DOM挂载到容器上;
  13. * @param {*} vdom 要渲染的虚拟DOM
  14. * @param {*} container 虚拟DOM转换成真实DOM,并插入到容器中
  15. */
  16. function render(vdom, container) {
  17. const dom = createDom(vdom)
  18. container.appendChild(dom)
  19. }
  20. /**
  21. * 虚拟dom变为真实dom
  22. * @param {*} vdom 虚拟dom
  23. */
  24. function createDom(vdom) {
  25. // 如果是字符串或者数字,直接返回真实节点
  26. if (['number', 'string'].includes(typeof vdom)) {
  27. return document.createTextNode(vdom)
  28. }
  29. // 否则它就是一个react元素了
  30. const { type, props } = vdom
  31. const dom = document.createElement(type)
  32. // 将虚拟dom属性更新到真实dom上
  33. updateProps(dom, props)
  34. // 如果儿子只有一个儿子,并且儿子是文本节点
  35. if (['number', 'string'].includes(typeof props.children)) {
  36. dom.textContent = props.children
  37. // 如果儿子是一个对象,并且是一个虚拟dom,递归调用render
  38. } else if (typeof props.children === 'object' && props.children.type) {
  39. // 把儿子变成真实dom,插入到自己身上
  40. render(props.children, dom)
  41. // 如果儿子是一个数组,说明儿子不止一个
  42. } else if (Array.isArray) {
  43. reconcileChildren(props.children, dom)
  44. } else {
  45. document.textContent = props.children ? props.children.toString() : ""
  46. }
  47. // // 把真实dom作为一个虚拟属性放在虚拟dom上,为以后更新做准备
  48. // vdom.dom = dom
  49. return dom
  50. }
  51. /**
  52. * @param {*} childrenVdom 儿子们的虚拟dom
  53. * @param {*} parentDom 父的真实dom
  54. */
  55. function reconcileChildren(childrenVdom, parentDom) {
  56. for (const childVdom of childrenVdom) {
  57. render(childVdom, parentDom)
  58. }
  59. }
  60. /**
  61. * 将虚拟dom属性更新到真实dom上
  62. * @param {*} dom 真实dom
  63. * @param {*} newProps 新属性
  64. */
  65. function updateProps(dom, newProps) {
  66. for (const key in newProps) {
  67. if (key === 'children') continue
  68. // style单独处理,因为dom不支持
  69. if (key === 'style') {
  70. const styleObj = newProps.style
  71. for (let attr in styleObj) {
  72. dom.style[attr] = styleObj[attr]
  73. }
  74. } else {
  75. dom[key] = newProps[key]
  76. }
  77. }
  78. }
  79. const ReactDom = { render }
  80. export default ReactDom

6、源代码

本文代码:https://gitee.com/linhexs/react-write/tree/1.react-render/

7、拓展

1、react源码篇:https://www.yuque.com/linhe-8mnf5/fxyxkm/xekk7b