1、修改react.js文件
修改处理文本节点方式,统一转换为对象:
src/utils.js文件wrapToVdom方法:
src/constants.js文件REACT_TEXT方法:
2、修改react-dom.js文件
2.1 修改createDom方法
修改文本处理方式:
/**
* 虚拟dom变为真实dom
* @param {*} vdom 虚拟dom
*/
export function createDom(vdom) {
// 否则它就是一个react元素了
const { type, props } = vdom
let dom
if (type === REACT_TEXT) {
dom = document.createTextNode(props.content)
} else if (typeof type === 'function') {
// 类组件
if (type.isReactComponent) {
return mountClassComponent(vdom)
} else {
// 函数组件直接返回
return mountFunctionComponent(vdom)
}
} else {
// 原生组件
dom = document.createElement(type)
}
// 将虚拟dom属性更新到真实dom上
updateProps(dom, {}, props)
// 如果儿子是一个对象,并且是一个虚拟dom,递归调用render
if (typeof props?.children === 'object' && props?.children?.type) {
// 把儿子变成真实dom,插入到自己身上
render(props?.children, dom)
// 如果儿子是一个数组,说明儿子不止一个
} else if (Array.isArray(props?.children)) {
reconcileChildren(props?.children, dom)
}
// 当根据vdom创建真实dom之后,真实dom挂载在dom属性上
vdom.dom = dom
return dom
}
2.2 修改深度比较方法
updateElement方法:
/**
* 深度比较两个虚拟dom
* @param {*} oldVdom 老的虚拟dom
* @param {*} newVdom 新的虚拟dom
*/
function updateElement(oldVdom, newVdom) {
// 文本节点
if (oldVdom.type === REACT_TEXT && newVdom.type === REACT_TEXT) {
let currentDom = newVdom.dom = oldVdom.dom // 复用老的真实dom节点
currentDom.textContent = newVdom.props.content // 直接修改老的dom节点文本
// 先更新属性
} else if (typeof oldVdom.type === 'string') { // 说明是原生div
let currentDom = newVdom.dom = oldVdom.dom //复用老的div的真实dom
updateProps(currentDom, oldVdom.props, newVdom.props) // 更新自己
// 更新儿子 只有原生组件 div span才会深度比较
updateChildren(currentDom, oldVdom.props.children, newVdom.props.children)
} else if (typeof oldVdom.type === 'function') {
if (oldVdom.type.isReactComponent) {
// 更新类组件
updateClassComponent(oldVdom, newVdom)
}else {
// 更新函数组件
updateFunctionComponent(oldVdom, newVdom)
}
}
}
updateChildren方法:
/**
* 深度比较儿子
* @param {*} parentDom 父dom节点
* @param {*} oldVChildren 老的儿子们
* @param {*} newVChildren 新的儿子们
*/
function updateChildren(parentDom, oldVChildren, newVChildren) {
// children可能是对象,也可能是数组,如果是对象就转为数组
oldVChildren = Array.isArray(oldVChildren) ? oldVChildren : [oldVChildren]
newVChildren = Array.isArray(newVChildren) ? newVChildren : [newVChildren]
const maxLength = Math.max(oldVChildren.length, newVChildren.length)
for (let i = 0; i < maxLength; i++) {
compareTwoVdom(parentDom, oldVChildren[i], newVChildren[i])
}
}
updateClassComponent方法:
function updateClassComponent(oldVdom, newVdom) {
let classInstance = newVdom.classInstance = oldVdom.classInstance;// 类的实例需要复用
newVdom.oldRenderVdom = oldVdom.oldRenderVdom // 上一次类组件渲染出来的虚拟dom
if (classInstance.componentWillReceivedProps) { // 组件将要接受到新的属性
classInstance.componentWillReceivedProps()
}
// 触发组件更新
classInstance.Updater.emitUpdate(newVdom.props)
}
updateFunctionComponent方法:
function updateFunctionComponent(oldVdom, newVdom) {
let parentDom = findDom(oldVdom).parentNode // 拿到真实dom父节点
let { type, props } = newVdom
let oldRenderVdom = oldVdom.oldRenderVdom // 老的渲染出来的vdom
let newRenderVdom = type(props) // 执行函数得到新的vdom
compareTwoVdom(parentDom, oldRenderVdom, newRenderVdom)
newVdom.oldRenderVdom = newRenderVdom
}
3、修改Component.js文件
classInstance.Updater.emitUpdate(newVdom.props)触发方法:
修改updateComponent方法:
添加nextProps参数:
4、源代码
本节代码:https://gitee.com/linhexs/react-write/tree/8.DOM-DIFF-COMPLETE/