实现三个接口:React.createElement, React.Component,ReactDom.render
在src目录下创建一个react目录 /src/react/index.js
**
function createElement() {
console.log(arguments)
}
const React = {
createElement
}
export default React;
在index.js里面引入 react/index.js
**
import React from "./react";
更新index.js:为createElement添加参数并返回结果对象
function createElement(type, props,...children){
// 父元素需要子元素返回结果,这里可以通过JSX编译后的代码得出结论
props.children = children;
return {type,props}
}
export default {createElement}
render
- 在src下创建一个react-dom /src/react-dom/index.js
- 创建一个render函数,能够将vdom渲染出来,这里先打印vnode
function render(vnode, container) {
container.innerHTML = `<pre>${JSON.stringify(vnode, null, 2)}</pre>`
}
export default {render}
在react-dom下创建一个react-vdom.js /src/react-dom/react-vdom.js
- 在当前文件下需要将虚拟dom转化成真实dom
- 处理原生标签、类组件、函数组件&&各种属性、事件
首先要区分一下type是=原生标签、类还是函数,声明一个createVNode方法去将虚拟dom包装一下
export function createVNode(vtype, type, props) {
const vnode = { vtype, type, props};
return vnode;
}
在react/index.js的createElement方法里去判断type类型 并将vtype做标识
import {createVNode} from '../react-dom/react-vdom';
function createElement(type, props, ...children) {
props.children = children
delete props.__source;
delete props.__self;
let vtype;
if (typeof type === 'string') {
//原生标签
vtype = 1;
} else if (typeof type === 'function') {
if (type.isClassCompoent) {
//类组件
vtype = 2;
} else {
//函数组件
vtype = 3;
}
}
return createVNode(vtype, type, props)
}
export class Component {
static isClassComponent = true;
constructor(props) {
this.props = props;
this.state = {};
}
}
看页面输出,每个对应的组件vdom已经有了标识,接下来就按这个标识对vdom进行处理
-
在react-vdom文件下 声明一个initVNode方法,将虚拟dom抓转化成真实dom
function initVNode(vnode) { const { vtype } = vnode; if (!vtype) { //没有vtype代表是文本节点 return document.createTextNode(vnode); } else if (vtype === 1) { //原生标签 return createElementTag(vnode) } else if (vtype === 2) { //class return createClassComp(vnode) } else if (vtype === 3) { //函数 return createFuncComp(vnode) } }
对这三个方法进行扩展处理
function createElementTag(vnode) {
const { type, props } = vnode;
const node = docunment.createElement(type);
//处理特殊属性
const { key, children, ...rest } = props;
Object.keys(rest).forEach(i => {
// 处理特殊属性,例如className、htmlfor、style等属性
if (i === 'className') {
node.setAttribute('class', rest[i]);
} else if (i === 'style' && typeof rest[i] === 'object') {
const style = Object.keys(rest[i]).map(s => s + ':' + res[i][s]).join(';');
node.setAttribute('style', style);
} else if (/on\w+/.test(i)) {
//通过正则对事件进行处理
const event = i.toLowerCase();
node[event] = rest[i];
} else {
node.setAttribute(i, rest[i]);
}
})
//递归渲染子元素
children.forEach(dom => {
if (Array.isArray(dom)) {
dom.forEach(n => node.appendChild(initVNode(dom)));
} else {
node.appendChild(initVNode(dom));
}
})
return node;
}
// 对class类声明的组件进行处理
function createClassComp(vnode) {
const {type, props} = vnode;
const vdom = new type(props).render();
return initVNode(vdom);
}
// 对function声明的组件进行处理
function createFuncComp(vnode) {
const {type, props} = vnode;
const vdom = new type(props);
return initVNode(vdom);
}
- 最后在/react-dom/index.js里面讲initVNode方法进行调用 看下页面结果
import {initVNode} from './react-vdom'
function render(vnode, container) {
//container.innerHTML = `<pre>${JSON.stringify(vnode, null, 2)}</pre>`
const node = initVNode(vnode);
container.appendChild(node);
}
export default {render}
jsx:
import React, {Component} from './react';
import ReactDOM from './react-dom';
console.log(ReactDOM)
function Comp (props) {
return <h2>{props.name}</h2>
}
class Comp2 extends Component {
render () {
return (
<div>
<h2>hi {this.props.name}</h2>
</div>
)
}
}
const users = [
{
name: '123',
age: 12
},
{
name: '1xzc23',
age: 13
}
]
const ele = (
<div className="active" style={{color: 'red'}} onClick={() => alert('clcik')}>
<span>hello</span>
<Comp name="hi,function"></Comp>
<Comp2 name="hi,class"></Comp2>
{/* <ul>
{users.map(item => {
return (
<li>{item.name}-{item.age}</li>
)
})}
</ul> */}
</div>
)
console.log(ele)
ReactDOM.render(ele, document.getElementById('root'));