美文网首页
实现一个简化版的React——从组件的复用讲起

实现一个简化版的React——从组件的复用讲起

作者: HolidayPeng | 来源:发表于2019-02-18 19:59 被阅读29次

    一、如何实现组件的复用?
    当我们需要一个按钮,点击之后提示当前时间,你可能会这么写:

      <body>
          <div class="container">
            <button class="btn"> What time is it? </button>
          </div>
          <script>
             document.querySelector('.btn').onclick = function() {
              alert(new Date());
            }
          </script>   
      </body>
    

    当有很多地方都会用到这段代码时,你可能会想到要把这块功能封装成一个组件,放在不同的父组件里。那么问题来了,怎么把子组件中的HTML部分和JavaScript部分分别放在父组件的HTML和JavaScript中呢?你可能会这么写:

    const DomStr = '<button class="btn"> What time is it? </button>';
    const container = document.createElement('div');
    container.innerHTML = DomStr;
    container.addEventListener('click', function(){
      alert(new Date())
    }, false);
    

    我们进一步把这段代码封装成一个方法,方便在父组件中导入:

    export function alertTimeButton(parentNode) {
      const domStr = '<button class="btn"> What time is it? </button>';
      const container = document.createElement('div');
      container.innerHTML = domStr;
      container.addEventListener('click', function(){
        alert(new Date())
      }, false);
      parentNode.appendChild(container);
    }
    

    这段代码做了四件事情:

    1. 把组件的HTML部分用字符串的形式保存在一个常量里;
    2. 创建一个DOM容器,把组件放进去;
    3. 绑定点击事件;
    4. 把装有组件的容器加到父组件的节点上。

    如果我们需要编写其他功能的组件,第1、2、4条是不是可以提出来作为公共的部分呢?我们是不是可以写一个Component类,然后让功能组件继承这个类呢?

    class Component {
      render (domStr, container) {
        const container = document.createElement('div');
        container.innerHTML = domStr;
        parentNode.appendChild(container);
    }
    class AlertTimeButton extends Component {
      constructor() {
        super();
      }
      alertTime() {
        alert(Date.now());
      }
      this.render(`<button id="btn" onclick="${this.alertTime.bind(this)}"> What time is it? </button>`, document.querySelector('.root'));
    }
    

    这样每次需要复用这个组件的时候就把它实例化,需要几个就实例化几个。
    以上是针对简单组件的封装,当遇到父子组件嵌套和数据传递的时候,我们又该怎么做呢?

    二、如何实现组件间数据的共享?
    我们把上面的需求改一下:

    1. 在父组件中显示当前时间;
    2. 点击父组件,更新当前时间;
    3. 点击父组件下的子组件按钮,弹窗提示父组件中显示的时间。
      于是有下面的结构:
            class Btn extends Component {
              constructor(props) {
                super(props);
              }
              alertTime() {
                alert(this.props.now);
              }
              render() {
                return `<button class="btn"> What time is it? </button>`;
              }
              componentDidMount() {
                document.querySelector('.btn').addEventListener('click', this.alertTime.bind(this), false)
              }
            }
            class Timer extends Component {
              constructor() {
                super();
                this.state = {
                  now: Date.now()
                }
              }
              updateTime() {
                this.setState({
                  now: Date.now()
                });
              }
              componentDidMount() {
                document.querySelector('.area').addEventListener('click', this.updateTime.bind(this), false)
              }
              render() {
                return (
                  `<div class="area"> It's ${this.state.now} 
                      <Btn now="${this.state.now}"/>
                   </div>`;
                )
              }
            }
    

    写到这里我们会遇到几个问题:

    1. 怎么把字符串模板中的组件和参数解析出来?
    2. 如何在state发生改变的时候重新渲染组件?
    3. 生命周期钩子,如componentDidMount在什么时候添加?

    对于第一个问题,JSX已经帮我们做了:


    JSX

    对于第二个问题,最简单粗暴的方式,就是在setState方法中重新渲染一遍组件, 同时在渲染结束后添加生命周期钩子:

            class Component {
              constructor(props) {
                this.props = props;
              }
              renderDom(parentNode) {
                const container = document.createElement('div');
                container.innerHTML = this.render();
                parentNode.appendChild(container)
                if (this.componentDidMount) this.componentDidMount()
              }
              setState(obj) {
                Object.keys(obj).forEach(key =>  {
                  if (this.state[key] !== obj[key]) {
                    this.state[key] = obj[key]; 
                    this.renderDom(parentNode);
                  }
                })
              }
            }
           
    

    这样当父组件连同子组件一起被重新渲染后,传递给子组件的props自然也被更新了。

    相关文章

      网友评论

          本文标题:实现一个简化版的React——从组件的复用讲起

          本文链接:https://www.haomeiwen.com/subject/jjfhsqtx.html