Appearance
断点调试
本节目标:理解“断点调试”的核心思路,并能把它用于实际开发或面试表达。 在
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);
}
}