React系列 --- 簡單模擬語法(一)

React系列

React系列 --- 簡單模擬語法(一)
React系列 --- Jsx, 合成事件與Refs(二)
React系列 --- virtualdom diff算法實現分析(三)
React系列 --- 從Mixin到HOC再到HOOKS(四)
React系列 --- createElement, ReactElement與Component部分源碼解析(五)
React系列 --- 從使用React瞭解Css的各類使用方案(六)
React系列 --- 從零構建狀態管理及Redux源碼解析(七)
React系列 --- 擴展狀態管理功能及Redux源碼解析(八)算法

前言

咱們先不講什麼語法原理,先根據API效果強行模擬語法使用,實現一個簡易版的React.segmentfault

render

第一步咱們先用類建立一個元素返回,而且綁定點擊事件,代碼以下,能夠正常看到一個按鈕出現了.app

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

實現類狀態和修改狀態方法dom

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輸出已經發現改變了,可是視圖並無渲染,那是由於這是結尾一次性插入,下面就渲染視圖這塊往下走this

從新渲染

咱們如今把插入數據的操做內置到class裏面,新增一個方法插入新元素移除舊元素.code

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());

如今效果雖然實現,可是仍是得開頭手動把元素插入視圖component

抽取公用類

咱們先將一些共有方法提取到一個單獨類繼承

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裏面,先略過不提.rem

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的通常語法實現了.

相關文章
相關標籤/搜索