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 } = vdomlet domif (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,递归调用renderif (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 = domreturn 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') { // 说明是原生divlet currentDom = newVdom.dom = oldVdom.dom //复用老的div的真实domupdateProps(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 // 上一次类组件渲染出来的虚拟domif (classInstance.componentWillReceivedProps) { // 组件将要接受到新的属性classInstance.componentWillReceivedProps()}// 触发组件更新classInstance.Updater.emitUpdate(newVdom.props)}
updateFunctionComponent方法:
function updateFunctionComponent(oldVdom, newVdom) {let parentDom = findDom(oldVdom).parentNode // 拿到真实dom父节点let { type, props } = newVdomlet oldRenderVdom = oldVdom.oldRenderVdom // 老的渲染出来的vdomlet newRenderVdom = type(props) // 执行函数得到新的vdomcompareTwoVdom(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/
