React原理(一)VirtualDom 到渲染UI

VirtualDom介绍

virtualDom实际就是个object类型的字典{} ,字典中包含typepropschildrentextContentkey来描述的是dom元素的。

如下所示真是的dom

<div className={'container'}><div>React</div>
</div> 

描述dom的VirtualDom,则为

 {type: "div",props: { className: "container" },children: [{type: "div",props: null,children: [{type: "text",props: {textContent: "React"}}]}]
 } 

VirtualDom生成过程

我们可以先看babel在线转换

<div name= '33'><div>{funcDoc}</div>
</div>

function fucDoc() {return <div>function component</div>
} 

可以看到上述代码经过babel转换后会调用React.createElement api

"use strict";

/*#__PURE__*/
React.createElement("div", {name: "33"
}, /*#__PURE__*/React.createElement("div", null, funcDoc));

function fucDoc() {return /*#__PURE__*/React.createElement("div", null, "function component");
} 

而调用React.createElement 的api后返回的刚好是我们想要的VirtualDOm 如下代码所示返回的刚好是VirtualDOm对象。

/**
 *
 * @param type 标签类型
 * @param props 标签的属性
 * @param children子元素
 * @returns {{children: *[], type, props}}
 */
export default function createElement (type, props, ...children) { const childElements = ...省略处理逻辑...return {type,props,childElements}
} 

实现tinyReact

环境配置

统一配置

我们需要告诉babel使用我们自己写的TinyReact,需要配置babel

在.babelrc文件中将React改为TinyReact

{"presets": ["@babel/preset-env",["@babel/preset-react",{"pragma": "TinyReact.createElement" //React改为TinyReact}]]
} 

通过以上配置告诉babel调用TinyReact.createElement来转换jsx文件

单个文件配置

除了统一配置外也可以在单个文件中添加 /** @jsx TinyReact.createElement */注释,来实现单个文件配置

生成VirtualDom

上一步操作会让babel调用我们自己写的createElement方法

//index.js文件

const virtualDOM = (<div className="container"><h1 test = "text">我是标题一</h1><h2 test = "text">我是标题二</h2><div>div嵌套一下 <div>里面的div</div></div>{shouldHide && <div>条件渲染1</div>}{!shouldHide && <div>隐藏我</div>}<button onClick={() => alert("你好")}>点我</button>2, 3<input type="text" value="13" /></div>
)
console.log(virtualDOM) 

如果createElement直接返回打印结果如下

可以看到文本被放在children中直接返回了,显然不对的,另外条件渲染!shouldHide为false被直接返回了,我们想要的是隐藏标签的效果

文本节点对应virtualDom为

{type: 'text',props: {textContent: '我是文本'}
} 
/**
 *
 * @param type 标签类型
 * @param props 标签的属性
 * @param children子元素
 * @returns {{children: *[], type, props}}
 */
export default function createElement (type, props, ...children) {const childElements = [].concat(...children).reduce((result,child) => {if (child !== false && child !== true && child !== null) {if (child instanceof Object) {result.push(child)} else {// 文本节点 手动调用 createElementresult.push(createElement("text", {textContent: child}))}} return result}, [])return {type,props: Object.assign({ children: childElements }, props),children: childElements}
} 

实现render方法渲染UI

//index.js

......
TinyReact.render(<virtualDOM />, root) 

//TinyReact/index.js

import createElement from "./createElement";
import render from "./render";

export default {render,createElement
} 

//TinyReact/render.js

import mountElement from "./mountElement";
export default function render(virtualDOM, container, oldDom) {mountElement(virtualDOM, container)
} 

//TinyReact/mountElement

import {isFunction} from "./utils";
import mountComponent from "./mountComponent";
import mountNativeElement from "./mountNativeElement";

export default function mountElement(virtualDOM, container) {if (isFunction(virtualDOM)) {// 判断是否为组件,组件的逻辑稍后实现 // mountComponent(virtualDOM, container, oldDOM)} else {mountNativeElement(virtualDOM, container, oldDOM)}
} 

//TinyReact/mountNativeElement

import createDOMElement from "./createDOMElement"

export default function mountNativeElement(virtualDOM, container) {const newElement = createDOMElement(virtualDOM)container.appendChild(newElement)
} 

//TinyReact/createDOMElement

export default function updateElementNode(element, virtualDOM) {const newProps = virtualDOM.propsObject.keys(newProps).forEach(propName => {const newPropsValue = newProps[propName]if (propName.slice(0, 2) === "on") {const eventName = propName.toLowerCase().slice(2)element.addEventListener(eventName, newPropsValue)} else if (propName === "value" || propName === "checked") {element[propName] = newPropsValue} else if (propName !== "children") {if (propName === "className") {element.setAttribute("class", newPropsValue)} else {element.setAttribute(propName, newPropsValue)}}})
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值