前言

咱们先不讲什么语法原理,先依据API成果强行模仿语法应用,实现一个简易版的React.

上面代码跟React源码没有关系,只是单纯模仿思路实现

render

第一步咱们先用类创立一个元素返回,并且绑定点击事件,代码如下,能够失常看到一个按钮呈现了.

class AddButton {  // 创立Dom  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 };  }  // 创立Dom  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);    return this.dom;  }}// 插入页面document.body.appendChild(new AddButton().render());

渲染之后看到this.dom输入曾经发现扭转了,然而视图并没有渲染,那是因为这是结尾一次性插入,上面就渲染视图这块往下走

从新渲染

咱们当初把插入数据的操作内置到class外面,新增一个办法插入新元素移除旧元素.

class AddButton {  // 内置状态  constructor() {    this.state = { num: 0 };  }  // 创立Dom  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());

当初成果尽管实现,然而还是得结尾手动把元素插入视图

抽取专用类

咱们先将一些共有办法提取到一个独自类,另外补全一下props属性传参

class React {  // 内置状态  constructor(props = {}) {    // 实例    this.wrapper = null    // 状态    this.state = {}    // 属性    this.props = {}  }  // 创立Dom  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", () => console.log('增加自定义事件'), false);    return this.dom;  }}

而后组件只须要间接继承Component而后解决本人逻辑即可

class AddButton extends React {  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>`;  }}

下面做了几件事:

  1. 抽取通用的逻辑到React类
  2. 自定义组件AddButton

还有一个问题是点击事件临时还是耦合进Component外面,下一章节实现

增加自定义事件

因为事件是由组件自定义的,所以咱们思路是在组件实例定义好之后,通用类绑定事件

class React {  // 内置状态  constructor(props = {}) {    // 实例    this.wrapper = null    // 状态    this.state = {}    // 属性    this.props = {}    // 事件    this.event = {}  }  // 创立Dom  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();  }  // 初始化事件  initEvent() {    const events = Object.keys(this.event)    events.forEach(key => {      this.dom.addEventListener(key, this.event[key].bind(this), false);    })  }  // 输入实例  _render(wrapper) {    if (wrapper) this.wrapper = wrapper;    this.dom = this.createDOM(this.render());    // 须要创立实例后能力初始化    this.dom && this.initEvent()    return this.dom;  }}

同时组件代码也须要绝对应的批改

class AddButton extends React {  constructor() {    super();    this.state = { num: 0 };    this.event = {      click: this.handleAdd    }  }  handleAdd() {    const num = this.state.num + 1;    this.setState({      num: num    });  }  render() {    return `<button id="btn">${this.state.num}</button>`;  }}

ReactDom.render

大家都晓得React会提供这么一个办法将组件插入一个指定元素,咱们间接模仿

const ReactDom = {  // 指定元素内插入组件实例  render(component, wrapper) {    wrapper.appendChild(component._render(wrapper));  }};

最终运行代码

咱们下面曾经实现了几个性能:

  1. 负责创立元素的React类,包含单向数据流,自定义状态,事件,替换元素等
  2. 负责挂载的ReactDom类
<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8" /></head><body>  <div id="root"></div>  <script>    class React {      // 内置状态      constructor(props = {}) {        // 实例        this.wrapper = null        // 状态        this.state = {}        // 属性        this.props = {}        // 事件        this.event = {}      }      // 创立Dom      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();      }      // 初始化事件      initEvent() {        const events = Object.keys(this.event)        events.forEach(key => {          this.dom.addEventListener(key, this.event[key].bind(this), false);        })      }      // 输入实例      _render(wrapper) {        if (wrapper) this.wrapper = wrapper;        this.dom = this.createDOM(this.render());        // 须要创立实例后能力初始化        this.dom && this.initEvent()        return this.dom;      }    }    const ReactDom = {      // 指定元素内插入组件实例      render(component, wrapper) {        wrapper.appendChild(component._render(wrapper));      }    };    class AddButton extends React {      constructor() {        super();        this.state = { num: 0 };        this.event = {          click: this.handleAdd        }      }      handleAdd() {        const num = this.state.num + 1;        this.setState({          num: num        });      }      render() {        return `<button id="btn">${this.state.num}</button>`;      }    }    ReactDom.render(new AddButton(), document.getElementById("root"));  </script></body></html>

至此,抛开理论思路不说,咱们曾经简略模仿进去React的根本语法实现了.