记笔记--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
}
注:代码逻辑并没有去做校验哈,这里都只是走的理想逻辑
标签:node,function,--,vnode,let,props,组件,记笔记 来源: https://blog.csdn.net/weixin_44038355/article/details/118730786