从传统的MVC架构来看,React其实是实现了View层的功能,根据数据的变化来渲染UI,实现真正的数据驱动,为了简化问题,我们先来看看React是如何渲染UI的。
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这两个函数,我们就能将文章开头的代码通过浏览器渲染出来了.
最后完整的代码请见github.com/wang-yulin/…