Skip to content

断点调试 ​

本节目标:理解“断点调试”的核心思路,并能把它用于实际开发或面试表达。 在src\index中,我们在类组件的button上添加了一个属性:

<button onClick={this.handleClick}>+</button>

从之前的react-dom的updateProps方法中,我们可以知道,我们对于以on开头的属性,会将其作为事件来处理,并在dom上添加相应的事件处理函数;这里要注意一下事件回调中的this指向问题;

function updateProps(dom, oldProps, newProps) {
  for (let key in newProps) {
    if (key === 'children') {//children
      continue;//

此处忽略子节点的处理

    } else if (key === 'style') {//style
      let styleObj = newProps[key];
      for (let attr in styleObj) {
        dom.style[attr] = styleObj[attr];
      }
    } else if (key.startsWith('on')) {
      //dom[key.toLocaleLowerCase()] = newProps[key];
      dom.addEventListener(key.toLocaleLowerCase().slice(2), newProps[key], false)
    } else {
      dom[key] = newProps[key];//className
    }
  }
}

在我们的类组件挂载之后,我们点击了按钮,这时候,dom上的click事件的回调函数将会执行,即执行handleClick函数;

handleClick = () => {
  debugger
  console.log('this', this)
  this.setState({ number: this.state.number + 1 });
  console.log(this.state);
}

在这个函数里,我们调用了setState,这将会调用类组件实例的updater.addState()方法;

// partialState

部分的状态

setState(partialState, callback) {
  this.updater.addState(partialState, callback);
}

在updater实例的addState方法中,我们只是把这个state记录到pendingStates数组中,然后再调用emitUpdate方法:

addState(partialState, callback) {
  ///

等待更新的或者说等待生效的状态

  this.pendingStates.push(partialState);
  if (typeof callback === 'function')
    //

状态更新后的回调

    this.callbacks.push(callback);
  this.emitUpdate();
}

在emitUpdate中,我们只是调用了一下updateComponent方法;

emitUpdate() {
  this.updateComponent();
}

在updateComponent方法中:

  • 我们取出updater中保存的组件实例,还有pendingStates数组;
  • 然后判断是不是有state值,如果有的话,我们调用shouldUpdate这个内部方法;
updateComponent() {
  let { classInstance, pendingStates } = this;
  if (pendingStates.length > 0) {
    shouldUpdate(classInstance, this.getState());
  }
}

在这里,我们调用了getState方法,这个方法做了一个State合并,并返回State值,这里要注意setState的参数是函数的情况,在遍历pendingStates的过程中,如果state值为一个函数,每次传入这个函数的State值将会是上一次合并过的新的State值;

getState() {
  let { classInstance, pendingStates } = this;
  let { state } = classInstance;
  pendingStates.forEach((nextState) => {
    if (typeof nextState === 'function') {
      //

如果是函数的话,每次调用取了上次的

State
      nextState = nextState(state);
    }
    state = { ...state, ...nextState };
  });
  //

清空等待生效的状态的数组

  pendingStates.length = 0;
  return state;
}

在shouldUpdate中,我们先把最新的State值保存在实例上,然后我们调用forceUpdate方法;

//

内部方法

function shouldUpdate(classInstance, nextState) {
  classInstance.state = nextState;
  classInstance.forceUpdate();
}

在forceUpdate方法中:

  • 我们取出oldRenderVdom;
  • 然后我们调用findDOM,目的是为了找到旧的真实DOM;
  • 接下来,我们调用组件的render方法,取得最新的renderVdom;
  • 最后,我们进行vdom对比;
  • 然后,我们把最新的renderVdom保存到实例的oldRenderVdom上;
forceUpdate() {
  let oldRenderVdom = this.oldRenderVdom;
  let oldDOM = findDOM(oldRenderVdom);
  let newRenderVdom = this.render();
  compareTwoVdom(oldDOM.parentNode, oldRenderVdom, newRenderVdom);
  this.oldRenderVdom = newRenderVdom;
}

compareTwoVdom是一个重要的方法,它来自react-dom方法,它是实现diff算法的地方;

/**
 * dom-diff

核心是比较新旧虚拟DOM的差异,然后把差异同步到真实DOM节点上

 * @param {*} parentDOM
 * @param {*} oldVdom
 * @param {*} newVdom
 */
export function compareTwoVdom(parentDOM, oldVdom, newVdom) {
  let oldDOM = findDOM(oldVdom);
  //

根据新的虚拟DOM得到新的真实

DOM
  let newDOM = createDOM(newVdom);
  //

把老的真实DOM替换为新的真实

DOM replaceChild

原生的DOM操作

  parentDOM.replaceChild(newDOM, oldDOM);
}

再看一下findDOM方法:

export function findDOM(vdom) {// TODO findDOM
  if (!vdom) return null;
  if (vdom.dom) {//vdom={type:'h1'}
    return vdom.dom;
  } else {
    //

类组件还是函数组件,他们虚拟DOM身上没有dom属性,但是有

oldRenderVdom
    return findDOM(vdom.oldRenderVdom);
  }
}

本站总访问 — 次