从0开始实现React系列之一(JSX)

114 阅读1分钟

从传统的MVC架构来看,React其实是实现了View层的功能,根据数据的变化来渲染UI,实现真正的数据驱动,为了简化问题,我们先来看看React是如何渲染UI的。

image.png

Learn By Coding,接下来通过我们自己实现的React,将如下代码渲染到浏览器...

import { React } from '../React'
import { ReactDOM } from '../ReactDOM'

const element = (
  <div id="app">
    <h1>Hello World!</h1>
    <p>Build your own react!</p>
  </div>
)

const container = document.getElementById("root")
ReactDOM.render(element, container)

首先要解决的是render函数的第一个输入element,这是React引入的新语法JSX,需要通过Babel转译才能通过编译,而Babel在进行转译的时候,实际上是调用React中的createElement函数,如下所示经过Babel转译后的element:

const element = React.createElement(
  "div",
  { id: "app" },
  React.createElement("h1", null, "Hello World!"),
  React.createElement("p", null, "Build your own react!")
)

为了Babel转译能顺利通过,我们就要实现createElement函数:

function createElement(type, config, children) {

  const props = { ...config }

  const childrenLength = arguments.length - 2
  if (childrenLength === 1) {
    props.children = children
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength)
    for (let i = 0; i < childArray.length; i++) {
      childArray[i] = arguments[i + 2]
    }
    props.children = childArray
  }

  return {
    type,
    props
  }
}

实际的ReactElement属性不止type、props这两个属性,但目前我们只需要关注这两个属性。搞定了JSX,我们接下来就着手处理render函数:

function render(element, container) {
  const { type, props } = element
  const dom = document.createElement(type)

  //handle props
  Object.keys(props).forEach((key) => {
    if (key !== "children") {
      dom[key] = props[key]
    }
  })
  //handle children
  const { children } = props
  if (children) {
    if (Array.isArray(children)) {
      children.forEach(child => render(child, dom))
    } else if (typeof children === "string") {
      const node = document.createTextNode(children)
      dom.appendChild(node)
    } else {
      render(children, dom)
    }
  }

  container.appendChild(dom)
}

有了createElement跟render这两个函数,我们就能将文章开头的代码通过浏览器渲染出来了.

image.png

最后完整的代码请见github.com/wang-yulin/…