Javascript

记笔记--React源码之路--实现从jsx、函数组件、类组件渲染到浏览器界面

本文主要是介绍记笔记--React源码之路--实现从jsx、函数组件、类组件渲染到浏览器界面,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
React版本号:17.0.1

目的:实现jsx、函数组件、类组件渲染到浏览器界面:手撸 ReactDOM.render

前提须知:我们需要了解虚拟DOM,React已经与babel合作,我们写的jsx会被babel转义为下图中的一个用于描述JavaScript节点的对象,虚拟DOM就张下图这样哈

在这里插入图片描述

界面图:

在这里插入图片描述

控制台查看真实DOM布局:

在这里插入图片描述

index.js文件代码:
// 模拟render方法文件引用
import TreactDom from './TestDemo/TreactDom'
// 模拟类组件继承文件引用
import Treact from './TestDemo/Treact'

// 模拟函数组件渲染
function FuncComponent(props) {
  return (
    <div className="func-border">
      <p>函数组件--{props.name}</p>
    </div>
  )
}

// 模拟类组件渲染
class ClassComponent extends Treact.Component {
  render () {
    return (
      <div className="calss-border">
        <p>类组件--{this.props.name}</p>
      </div>
    )
  }
}

const JSX = (
  <div className="div">
    <h1 id="h1">我是h1</h1>
    <span style={{
      color: 'red',
      background: 'pink'
    }}>hello</span><br/>
    我是纯文本<br/>

    <FuncComponent name="func" />
    <ClassComponent name="class" />
  </div>
)

TreactDom.render(JSX, document.getElementById('root'))
TreactDom.js文件代码:
function render (vnode, container) {
  console.log('vnode: ', vnode)
  // vnode-->node
  let node = createNode(vnode)
  // node插入到container中
  container.appendChild(node)
}

// 由虚拟dom生成真实dom的函数
function createNode (vnode) {
  let {type} = vnode
  let node = null

  if (typeof type === 'string') { // 创建标签节点:div、p、a等等
    node = updateHostComponent(vnode)
  } else if (typeof type === 'function') { // 函数、类组件
    node = type.prototype.isReactComponet ? updateClassComponent(vnode) : updateFuncComponent(vnode)
  } else { // 创建文本节点
    node = updateTextNode(vnode)
  }

  return node
}

// 类组件
function updateClassComponent(vnode) {
  let {type, props} = vnode
  let instance = new type(props)
  let vvnode = instance.render()
  let node = createNode(vvnode)

  return node
}

// 函数组件
function updateFuncComponent(vnode) {
  let {type, props} = vnode
  let vvnode = type(props)
  let node = createNode(vvnode)

  return node
}

// 标签节点
function updateHostComponent(vnode) {
  let {type, props} = vnode
  let node = document.createElement(type)

  updateNodeProps(props, node)

  reconcileChildren(props.children, node)

  return node
}

// 给真实dom上添加属性:style、className、id等等
function updateNodeProps(props, node) {
  Object.keys(props).filter(key => key != 'children').forEach(key => {

    if (key == 'style') {
      let str = Object.keys(props.style).reduce((prev, k) => {
        prev.push(`${k}: ${props.style[k]}`)
        return prev
      }, []).join(';')
      node.style = str
    } else {
      node[key] = props[key]
    }
  })
}

// 遍历 props.children,利用递归的思路将子虚拟dom节点生成真实dom,并且挂载到真实dom父节点上
function reconcileChildren(children, container) {
  if (!children) return

  const newChildren = Array.isArray(children) ? children : [children]
  
  for (let i = 0; i < newChildren.length; i++) {
    const vnode = newChildren[i];

    render(vnode, container)
  }
}

// 创建文本节点
function updateTextNode(vnode) {
  return document.createTextNode(vnode)
}


export default {
  render
}
Treact.js文件代码:
function Component(props) {

  // 这里需要将props挂载到this上,你的类组件才能使用  this.props 哈
  this.props = props
}

// 表示是类组件:为啥不用 Boolean 值:因为源码是这样标识的,应该是react发展的历史原因吧
Component.prototype.isReactComponet = {}

export default {
  Component
}

注:代码逻辑并没有去做校验哈,这里都只是走的理想逻辑

这篇关于记笔记--React源码之路--实现从jsx、函数组件、类组件渲染到浏览器界面的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!