【React源码】Day02——实现事件绑定和同步状态更新

【React源码】Day02——实现事件绑定和同步状态更新

实现事件绑定

githubreact

// react-dom.js
function updateDOMAttr(dom, props) {
  for (let key in props) {
    if (key === "children") {
      continue;
    }
    if (key === "style") {
      for (let attr in props[key]) {
        dom.style[attr] = props[key][attr];
      }
    } else if (key.startsWith("on")) {
      // onClick => onclick
      dom[key.toLocaleLowerCase()] = newProps[key]
    } else {
      dom[key] = props[key];
    }
  }
}

实现 setState()

同步更新

githubgit

  • 当调用 this.setState() 时,实际调用 Component 类的 setState
  • 建立 Updater 类
    • 用于收集状态 addState(),this.pendingStates = []
    • 合并状态,getState()
    • 触发视图进行强制刷新,updateComponent()
  • Component 类中 forceUpdate() 强制更新视图,将虚拟 dom 转化为真实 dom,挂载到视图
// Component.js
class Updater {
  constructor(classInstance) {
    // 类组件实例
    this.classInstance = classInstance;
    // 收集状态
    this.pendingStates = [];
  }

  addState(partialState) {
    this.pendingStates.push(partialState);
    // 若是当前处于批量更新模式
    updateQueen.isBatchingUpdate
      ? updateQueen.add(this)
      : this.updateComponent();
  }

  updateComponent() {
    let { classInstance } = this;
    if (this.pendingStates.length > 0) {
      // 替换 state 为最新的状态
      classInstance.state = this.getState();
      // 强制视图进行更新
      classInstance.forceUpdate();
    }
  }
  // 获取最新的状态
  getState() {
    let { classInstance, pendingStates } = this;
    let { state } = classInstance;
    if (pendingStates.length > 0) {
      pendingStates.forEach((nextState) => {
        if (isFunction(nextState)) {
          nextState = nextState(state);
        } else {
          state = { ...state, ...nextState };
        }
      });
      pendingStates.length = 0;
    }
    return state;
  }
}
// Component.js
class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
    this.state = {};

    this.updater = new Updater(this);
  }

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

  // 更新视图
  forceUpdate() {
    // 此时 renderVdom 已是更新后的虚拟 DOM(state已经更新)
    let renderVdom = this.render();
    updateClassComponent(this, renderVdom);
  }
}

function updateClassComponent(classInstance, renderVdom) {
  let oldDOM = classInstance.dom;
  let newDOM = createDOM(renderVdom);
  oldDOM.parentNode.replaceChild(newDOM, oldDOM);
  classInstance.dom = newDOM;
}

异步更新

githubgithub

  • 添加队列函数 updateQueen 使用 add() 收集 updaters 并使用 batchUpdate() 更新(相似发布订阅)dom

  • 修改 index.js 文件内容,手动改变 isBatchingUpdate 的值,并手动清空队列更新(这里有点 low )异步

// index.js
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }
  handleClick = () => {
    updateQueen.isBatchingUpdate = true;
    this.setState({ number: this.state.number + 1 });
    console.log(this.state);

    setTimeout(() => {
      updateQueen.batchUpdate();
    });
  };
  render() {
    return (
      <div>
        <p>number:{this.state.number}</p>
        <button onClick={this.handleClick}>点击</button>
      </div>
    );
  }
}
export const updateQueen = {
  updaters: [],
  isBatchingUpdate: false,
  add(updater) {
    this.updaters.push(updater);
  },
  batchUpdate() {
    this.updaters.forEach((updater) => updater.updateComponent());
    this.isBatchingUpdate = true;
  },
};
相关文章
相关标签/搜索