乐趣区

关于javascript:React系列二-React基本语法实现思路

前言

咱们先不讲什么语法原理, 先依据 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 的根本语法实现了.

退出移动版