美文网首页
React的简单实现(二)组件

React的简单实现(二)组件

作者: 不懂量化的吃货不是好前端 | 来源:发表于2020-05-10 11:26 被阅读0次

上一篇我们已经实现了简单的createElement,借助词法分析库将JSX转换成JS对象:https://www.jianshu.com/p/1c6bc9171b0c

如果JSX片段中的某个元素是组件,那么createElement的第一个参数tag将会是一个方法,而不是字符串。

function createElement( tag, attrs, ...children ) {
    return {
        tag,
        attrs,
        children
    }
}

例如在处理<Count initCount="1" />时,createElement方法的第一个参数tag,实际上就是我们定义Count的方法:

class Count extends React.Component {
    render() {
        return <h1>Count: {this.props.initCount}</h1>;
   

Component实现

React.Component包含了一些预先定义好的变量和方法

class Component {}
state & props

React.Component定义的组件有自己的state和props,可以通过this访问
所以在构造函数中,需要初始化state和props,挂接到this上面

class Component {
    constructor( props = {} ) {
        this.state = {};
        this.props = props;
    }
}
setState
# 后面再实现
const renderComponent = (component)=>{}
class Component {
    constructor( props = {} ) {
        // ...
    }

    setState( stateChange ) {
        // 将修改合并到state
        Object.assign( this.state, stateChange );
        renderComponent( this );
    }
}
render

需要修改之前的ReactDOM.render方法,让其支持渲染组件。
这是上一篇提到的render:

function render( vnode, container ) {
    return container.appendChild( _render( vnode ) );
}

function _render( vnode ) {

    if ( vnode === undefined || vnode === null || typeof vnode === 'boolean' ) vnode = '';

    if ( typeof vnode === 'number' ) vnode = String( vnode );

    if ( typeof vnode === 'string' ) {
        let textNode = document.createTextNode( vnode );
        return textNode;
    }

    const dom = document.createElement( vnode.tag );

    if ( vnode.attrs ) {
        Object.keys( vnode.attrs ).forEach( key => {
            const value = vnode.attrs[ key ];
            setAttribute( dom, key, value );
        } );
    }

    vnode.children.forEach( child => render( child, dom ) );    // 递归渲染子节点

    return dom; 
}

增加一个对tag的识别

function createComponent( component, props ) {

    let inst;
    // 如果是类定义组件,则直接返回实例
    if ( component.prototype && component.prototype.render ) {
        inst = new component( props );
    // 如果是函数定义组件,则将其扩展为类定义组件
    } else {
        inst = new Component( props );
        inst.constructor = component;
        inst.render = function() {
            return this.constructor( props );
        }
    }

    return inst;
}
function _render( vnode ) {

    // ...

    if ( typeof vnode.tag === 'function' ) {

        const component = createComponent( vnode.tag, vnode.attrs );

        setComponentProps( component, vnode.attrs );

        return component.base;
    }
    
    // ...
}
// set props
function setComponentProps( component, props ) {

    if ( !component.base ) {
        if ( component.componentWillMount ) component.componentWillMount();
    } else if ( component.componentWillReceiveProps ) {
        component.componentWillReceiveProps( props );
    }

    component.props = props;

    renderComponent( component );

}

定义renderComponent函数,在里面挂接更新生命周期函数,setstate时进行调用

export function renderComponent( component ) {

    let base;

    const renderer = component.render();

    if ( component.base && component.componentWillUpdate ) {
        component.componentWillUpdate();
    }

    base = _render( renderer );

    if ( component.base ) {
        if ( component.componentDidUpdate ) component.componentDidUpdate();
    } else if ( component.componentDidMount ) {
        component.componentDidMount();
    }

    if ( component.base && component.base.parentNode ) {
        component.base.parentNode.replaceChild( base, component.base );
    }

    component.base = base;
    base._component = component;

}

完整JS代码为:

function createElement(tag, attrs, ...children) {
  return {
    tag,
    attrs,
    children,
  };
}

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

  setState(stateChange) {
    // 将修改合并到state
    Object.assign(this.state, stateChange);
    renderComponent(this);
  }
}

function createComponent(component, props) {

  let inst;
  // 如果是类定义组件,则直接返回实例
  if (component.prototype && component.prototype.render) {
    inst = new component(props);
    // 如果是函数定义组件,则将其扩展为类定义组件
  } else {
    inst = new Component(props);
    inst.constructor = component;
    inst.render = function() {
      return this.constructor(props);
    };
  }

  return inst;
}

function setComponentProps(component, props) {

  if (!component.base) {
    if (component.componentWillMount) component.componentWillMount();
  } else if (component.componentWillReceiveProps) {
    component.componentWillReceiveProps(props);
  }

  component.props = props;

  renderComponent(component);

}

function renderComponent(component) {

  let base;

  const renderer = component.render();

  if (component.base && component.componentWillUpdate) {
    component.componentWillUpdate();
  }

  base = _render(renderer);

  if (component.base) {
    if (component.componentDidUpdate) component.componentDidUpdate();
  } else if (component.componentDidMount) {
    component.componentDidMount();
  }

  if (component.base && component.base.parentNode) {
    component.base.parentNode.replaceChild(base, component.base);
  }

  component.base = base;
  base._component = component;

}

const React = {
  createElement,
  createComponent,
  Component,
};

function render(vnode, container) {
  return container.appendChild(_render(vnode));
}

function _render(vnode) {

  if (vnode === undefined || vnode === null || typeof vnode === 'boolean') vnode = '';

  if (typeof vnode === 'number') vnode = String(vnode);

  if (typeof vnode === 'string') {
    let textNode = document.createTextNode(vnode);
    return textNode;
  }
  if (typeof vnode.tag === 'function') {

    const component = createComponent(vnode.tag, vnode.attrs);

    setComponentProps(component, vnode.attrs);

    return component.base;
  }

  const dom = document.createElement(vnode.tag);

  if (vnode.attrs) {
    Object.keys(vnode.attrs).forEach(key => {
      const value = vnode.attrs[key];
      setAttribute(dom, key, value);
    });
  }

  vnode.children.forEach(child => render(child, dom));    // 递归渲染子节点

  return dom;
}

function setAttribute(dom, name, value) {
  // 如果属性名是className,则改回class
  if (name === 'className') name = 'class';

  // 如果属性名是onXXX,则是一个事件监听方法
  if (/on\w+/.test(name)) {
    name = name.toLowerCase();
    dom[name] = value || '';
    // 如果属性名是style,则更新style对象
  } else if (name === 'style') {
    if (!value || typeof value === 'string') {
      dom.style.cssText = value || '';
    } else if (value && typeof value === 'object') {
      for (let name in value) {
        // 可以通过style={ width: 20 }这种形式来设置样式,可以省略掉单位px
        dom.style[name] = typeof value[name] === 'number' ? value[name] + 'px' : value[name];
      }
    }
    // 普通属性则直接更新属性
  } else {
    if (name in dom) {
      dom[name] = value || '';
    }
    if (value) {
      dom.setAttribute(name, value);
    } else {
      dom.removeAttribute(name);
    }
  }
}

非框架代码是下面的,可以运行一下:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, { this.props.name }</h1>;
  }
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
    element,
    document.getElementById( 'root' )
);

运行结果为:


image.png

加一段更新代码试试,绑定事件onClick是通过setAttribute加上去的,本质就是document.createElement('p')['onclick'] = ()=>{}实现

import React from './react';
// end with react frame work
class Count extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0,
    }
  }
  render() {
    return <div>
      <div>Count: {this.state.count}</div>
      <button
        onClick={()=>{
          this.setState({
            count: this.state.count+1,
          })
        }}
      >
        Add
      </button>
    </div>;
  }
}

const ReactDOM = {
  render: (vnode, container) => {
    container.innerHTML = '';
    return React.render(vnode, container);
  },
};
const element = <Count name="Sara"/>;
console.log(element);
ReactDOM.render(
  element,
  document.getElementById('root'),
);

运行结果为


image.png

源码地址:https://github.com/liuxiaocong/dailyMove/commit/c7bb2c8ae4d1f29af04503291e929402207122c3

相关文章

  • React的简单实现(二)组件

    上一篇我们已经实现了简单的createElement,借助词法分析库将JSX转换成JS对象:https://www...

  • react-native-webview 之双向通信

    react-native 通过 WebView 组件可以非常简单的实现通信,这里通过从RN中分离出来的react-...

  • react组件

    一、什么是组件 组件(Component)是对数据和方法的简单封装,react还包括对html的简单的简单封装 二...

  • 如何创建React组件并发布到npm?

    实现步骤: 创建React组件项目; 创建测试项目并引用组件; 发布React组件到npm上; 一、创建React...

  • React的组件通信和特点

    一、React应用的架构图 二、组件通信的实现 首先,组件通信只能实现上下两层组件的直接通信,如果不是上下两层组件...

  • React项目实现点击图片预览

    1、React项目实现点击图片预览:“React-zmage” 一个图片放大查看组件,动画流畅简洁,使用简单方便。...

  • React Native 基础组件之 StyleSheet

    在 React Native 中,StyleSheet 组件是实现了类似 Web 中 CSS 样式表的功能。简单的...

  • 拯救react的hooks:react的问题和hooks的作用

    react组件和react组件逻辑复用 react是一个视图层组件方案,最核心的功能就是绑定视图与数据和逻辑,实现...

  • react学习笔记

    React笔记 创建项目 入口文件 组件构成 子组件对父组件的校验等 React的虚拟Dom实现原理 state数...

  • 多项选择题组件--react

    最近在一本react书上看到一个多项选择题的react组件,做了一些改动。实现一个多项选择题的组件很简单,但是这里...

网友评论

      本文标题:React的简单实现(二)组件

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