乐趣区

React简单模拟语法一

前言

我们先不讲什么语法原理, 先根据 API 效果强行模拟语法使用, 实现一个简易版的 React.

render

第一步我们先用类创建一个元素返回, 并且绑定点击事件, 代码如下, 可以正常看到一个按钮出现了.

class AddButton {createDOM(domString) {const div = document.createElement("div");
    div.innerHTML = domString;
    return div;
  }

  render() {this.dom = this.createDOM(`<button>0</button>`);
    this.dom.addEventListener("click", () => console.log("click"), false);
    return this.dom;
  }
}

document.body.appendChild(new AddButton().render());

state && setState

实现类状态和修改状态方法

class AddButton {constructor() {this.state = { num: 0};
  }

  createDOM(domString) {const div = document.createElement("div");
    div.innerHTML = domString;
    return div;
  }

  setState(state) {
    this.state = state;
    this.dom = this.render();}

  handleAdd() {
    const num = this.state.num + 1;
    this.setState({num: num});
  }

  render() {this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`);
    this.dom.addEventListener("click", () => this.handleAdd(), false);
    console.log(this.dom);
    return this.dom;
  }
}

document.body.appendChild(new AddButton().render());

渲染之后看到 this dom 输出已经发现改变了, 但是视图并没有渲染, 那是因为这是结尾一次性插入, 下面就渲染视图这块往下走

重新渲染

我们现在把插入数据的操作内置到 class 里面, 新增一个方法插入新元素移除旧元素.

class AddButton {constructor() {this.state = { num: 0};
  }

  createDOM(domString) {const div = document.createElement("div");
    div.innerHTML = domString;
    return div;
  }

  changeDom() {
    const oDom = this.dom;
    this.dom = this.render();
    document.body.insertBefore(this.dom, oDom);
    document.body.removeChild(oDom);
  }

  setState(state) {
    this.state = state;
    this.changeDom();}

  handleAdd() {
    const num = this.state.num + 1;
    this.setState({num: num});
  }

  render() {this.dom = this.createDOM(`<button id="btn">${this.state.num}</button>`);
    this.dom.addEventListener("click", () => this.handleAdd(), false);
    return this.dom;
  }
}

document.body.appendChild(new AddButton().render());

现在效果虽然实现, 但是还是得开头手动把元素插入视图

抽取公用类

我们先将一些共有方法提取到一个单独类

class Component {constructor() {}

  createDOM(domString) {const div = document.createElement("div");
    div.innerHTML = domString;
    return div;
  }

  changeDom() {
    const oDom = this.dom;
    this.dom = this._render();
    this.wrapper.insertBefore(this.dom, oDom);
    this.wrapper.removeChild(oDom);
  }

  setState(state) {
    this.state = state;
    this.changeDom();}

  _render(wrapper) {if (wrapper) this.wrapper = wrapper;
    this.dom = this.createDOM(this.render());
    this.dom.addEventListener("click", () => this.handleAdd(), false);
    return this.dom;
  }
}

然后组件只需要直接继承 Component 然后处理自己逻辑即可

class AddButton extends Component {constructor() {super();
    this.state = {num: 0};
  }

  handleAdd() {
    const num = this.state.num + 1;
    this.setState({num: num});
  }

  render() {return `<button id="btn">${this.state.num}</button>`;
  }
}

还有一个问题是点击事件暂时还是耦合进 Component 里面, 先略过不提.

ReactDom.render

大家都知道 React 会提供这么一个方法将组件插入一个指定元素, 我们直接模拟

const ReactDom = {render(component, wrapper) {wrapper.appendChild(component._render(wrapper));
  }
};

ReactDom.render(new AddButton(), document.getElementById("root"));

Props

还有一个重要的传输数据实现如下

const ReactDom = {render(component, wrapper) {wrapper.appendChild(component._render(wrapper))
  }
}

class Component {constructor(props = {}) {this.props = props}

  createDOM(domString) {const div = document.createElement("div")
    div.innerHTML = domString
    return div
  }

  changeDom() {
    const oDom = this.dom
    this.dom = this._render()
    this.wrapper.insertBefore(this.dom, oDom)
    this.wrapper.removeChild(oDom)
  }

  setState(state) {
    this.state = state
    this.changeDom()}

  _render(wrapper) {if (wrapper) this.wrapper = wrapper
    this.dom = this.createDOM(this.render())
    this.dom.addEventListener("click", () => this.handleAdd(), false)
    return this.dom
  }
}

class AddButton extends Component {constructor(props) {super(props)
    this.state = {num: 0}
  }

  handleAdd() {
    const num = this.state.num + 1
    this.setState({num: num})
  }

  render() {console.log(this.props)
    return `<button id="btn">${this.state.num}</button>`
  }
}

ReactDom.render(new AddButton({a:1}), document.getElementById("root"))

至此, 抛开实际思路不说, 我们已经简单模拟出来 React 的一般语法实现了.

退出移动版