前一章实现了createElement方法, 这一节我们来实现render方法。
react中通过render方法将VirtualDOM转化成真实DOM,渲染到页面上。下面我们来实现下render方法。
大致思路就是把虚拟DOM通过方法转化为真实DOM再append到指定的node节点中。
首先我们需要区分下传入的virtualDOM是组件还是普通的node节点。不论是类组件还是函数组件,type类型都是function。而普通的node节点type类型则是string.所以我们可以通过type类型来区分组件和普通node节点。
// 不论是类组件还是函数组件 type类型都是function
export default function isFunction(virtualDOM) {
return virtualDOM && typeof virtualDOM.type === 'function'
}
先来实现普通节点的情况,此时要考虑该节点是否是文本类型,如果是文本类型的话,则调用document.createTextNode
方法创建文本节点。如果是普通节点类型,则调用document.createElement
我们来定义一个函数来实现转化虚拟DOM的功能
//mountElement.js
export default function mountElement(virtualDOM, container) {
let handleElement = null;
if(!isFunction(virtualDOM)) {
handleElement = mountDOMElement(virtualDOM);
}
container.appendChild(handleElement);
}
//mountDOMElement.js 这个方法之后会复用,所以单独提出来一个函数
export default function mountDOMElement() {
let handleElement = null;
// 文本节点
if(virtualDOM.type === 'text') {
handleElement = document.createTextNode(virtualDOM.props.textContent);
} else {
handleElement = document.createElement(virtualDOM.type);
}
// 递归的实现子节点的创建
virtualDOM.children.forEach(child => {
// 调用这个mountElement进行递归,是因为要判断子节点是否是组件。
mountElement(child, handleElement)
})
return handleElement
}
// render.js
// 先不考虑diff
export default function render(virtualDOM, container) {
// 用来接受真实的dom
// 最后将节点append到指定的容器中
mountElement(virtualDOM, container)
}
现在我们以及基本实现了render方法渲染普通节点元素,此时还有一个问题,就是我们标签的属性并没有添加上,所以我们再新建一个文件,来为元素添加属性。
/**
* @description: 为元素添加属性
* @param {*}
* @return {*}
*/
export default function updateNodeElement(newElement, virtualDOM) {
const newProps = virtualDOM.props;
Object.keys(newProps).forEach((propName) => {
const newPropsValue = newProps[propName];
// / 判断属性是否是事件属性 例如onClick转化为click,通过addEventListener来进行事件绑定
if (propName.slice(0, 2) === 'on') {
const eventName = propName.toLowerCase().slice(2);
newElement.addEventListener(eventName, newPropsValue);
} else if (propName === 'value' || propName === 'checked') {
// 表单元素的属性
newElement[propName] = newPropsValue;
} else if (propName !== 'children') {
if (propName === 'className') {
newElement.setAttribute('class', newPropsValue);
} else {
newElement.setAttribute(propName, newPropsValue);
}
}
});
}
至此普通节点的render方法基础已经实现,下一节我们将介绍组件的render转化。