React源码——渲染(render && createElement)的简单实现
前言
当前市面上公司对React需求越来越大主, 对于React的源码学习必须提上日程
初始化项目
-
React脚手架创建项目
- 全局安装npm install -g create-react-app
- 创建项目create-react-app My-React-App
- 删除不需要的文件
-
目录文件如下
-
运行热更新报错,解决方案React v17 热刷新 不起作用, 或者在package.json的脚本中加上
"start": "FAST_REFRESH=false react-scripts start",
react创建以及分析创建虚拟dom,babel转化的原理babel试一试
-
Jsx----->babel----->js方法------>React.createElement()------->vdom
-
React.createElement实现
// react.js
import get$$Type from './stants';
/* @param type 元素类型
* @param props 元素的配置对象
* @param children 元素的子元素们
*/
function createElement(type,props,children){
const { key, ref, ...resprops } = props ? props : {};
const len = arguments.length;
if(len > 3){
// 多个children
resprops.children = Array.prototype.slice.call(arguments,2)
} else if(len === 3)r
// children
resprops.children = children;
}else{
// 没有children
// do nothing
}
return {
$$typeof:get$$Type(type),
key,
ref,
type,
props: resprops
}
}
const React = {
createElement
}
export default React
// stants.js
const REACT_TEXT = Symbol("react.TEXT");
const REACT_ELEMENT = Symbol("react.element");
const get$$Type = (type) =>{
return type ? REACT_ELEMENT : REACT_TEXT;
}
export default get$$Type
编写reactDOM.render方法,实现想虚拟dom转化成真实的dom
- vdom转化成真实dom
- 给真实dom赋值属性
- 递归childrenDom
- 内容append到对应的容器
// react-dom.js
// 渲染真是dom到页面
function render(vdom,container){
const reldom = createDom(vdom);
container.appendChild(reldom)
}
// 处理函数式组件
const mountFunctionComponent = (vdom) => {
const { type ,props } = vdom;
// 获取到函数的vdom
let functionVdom = type(props);
return createDom(functionVdom)
}
// 初始化以及更新属性
const updateOtherProps = (relDom,oldProps,newProps) => {
if(newProps){
for(let key in newProps){
if(key === 'style'){
for(let k in newProps[key]){
relDom.style[k] = newProps[key][k]
}
} else {
relDom[key] = newProps[key]
}
}
}
// 更新处理
if(oldProps){
// 旧得属性在新的属性中没有删除
for(let key in oldProps){
if(!newProps[key]){
relDom[key] = null
}
}
}
}
const createDom = (vdom) => {
let relDom;
// 纯文本
if(!vdom?.type){
return document.createTextNode(vdom);
}
// ReactFragment
if( typeof vdom?.type === 'symbol'){
return document.createTextNode(vdom?.props?.children);
}
const { type, props: { children,...resProps } } = vdom;
// 函数组件
if(typeof type == 'function') {
// functionComponent---> 变成vdom
return mountFunctionComponent(vdom)
} else {
// dom 节点
relDom = document.createElement(type);
}
// 赋值属性
if(resProps){
updateOtherProps(relDom,{},resProps)
}
// 递归childrenDom
if(children){
renderChildren(children,relDom);
}
return relDom;
}
const renderChildren = (children,dom) => {
// 多个children
if(Array.isArray(children)){
for(let index in children){
render(children[index],dom)
}
} else {
render(children,dom)
}
}
const ReactDom = { render }
export default ReactDom
使用
import React from './react';
import ReactDOM from './react-dom';
import './index.css';
const App =
<div className='app' key='app' style={{ color:'peru',background:'mediumpurple' }}>
<span style={{ color:'yellow' }}>
app<b style={{ color:'plum' }}>7777</b>
</span>
<h3 style={{ color:'orange' }}>111</h3>
<span style={{ color:'ghostwhite' }}>apps</span>
333
<>6666</>
</div>;
ReactDOM.render(App, document.getElementById('root'));