2

Deep In React (二) Component,Element和渲染

 2 years ago
source link: https://hateonion.me/posts/8e61/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在上一篇文章中,我们谈到了如何从应用层面优化React的性能,在随后的几篇文章中,我们将深入React的底层实现,仔细分析一下为什么React会有如此高的性能。在介绍React底层的reconciliation算法之前,我们需要先了解一些先导知识。

先导知识: React Component和React Element

在React中,有着组件(component)和元素(element)两个概念。我们日常可能听到过很多次Component这个名词,但是对于Element这个名词在React中似乎聊得并不多。今天就来给大家讲讲什么是Element,什么是Component,以及他们和React渲染的关系。

React Element

React Element从数据结构上来讲就是简单的plain object, 这个plain object会提供对于整个React组件树的描述。

{
  type: 'button',
  props: {
    classNames: 'foo',
    children: {
      type: 'span',
      props: {
        children: 'click'
      }
    }
}

讲这个Element翻译成HTML就是如下结构

<button class="foo">
  <span>click</span>
</button>

需要注意的是type这个属性,在上面的例子中,type是一个string,而type是string的element React将其定义为HostElement。Host的意思是这个元素会根据不同的平台对应不同的Native View。比如ReactDOM会对应成相应的HTML Tag,而React-Native则会对应成Native View。

而除开HostElement, React还支持Component类型的element。我们在React代码中经常会这样组合我们的组件。

  <Container>
    <Header />
    <Sidebar />
    <div>content</div>
    <Footer />
  </Container>

这段代码对应生成的element的描述如下

{
  type: Container,
  props: {
    children: [
      {
        type: Header,
        props: null
      },
      {
        type: Sidebar,
        props: null,
      },
      {
        type: 'div'
        props: {
          children: 'content'
        }
      },
      {
        type: Footer,
        props: null
      }
    ]
  }
}

这里的Container/Header/Sidebar/Footer对应的可能是函数或者是class, 而这个函数或者class就是我们经常谈到的React Component。

React Component

Component事实上是对于React Element Tree的一种封装。假设我们要渲染Button这个component。

const Button = content => <button>{content}</button>

render(<Button content="foo" />

事实上React会将Button这个component解析成以下element树

{
  type: Button,
  props: {
    content: {
      content: 'foo'
    }
  }
}

而React发现Button其实是一个Component Element,而不是一个Host Element,所以React会递归向下渲染。

{
  type: button,
  props: {
    children: 'foo'
  }
}

React会一直这样递归下去直到所有的Component Element都被翻译成Host Element。

component的function写法和class写法

Component事实上有三种写法,分别是function写法,class without es6以及es6 class写法

// stateless component
const Button = content => ({
  type: 'button',
  props: {
    content
  }
})

//  class without es6
const Button = React.CreateClass({
  render() {
    const { content } = this.props;
    return {
      type: 'button',
      props: {
        content
      }
    }

  }
})

// es6 class
class Button extends React.Component {
  reder() {
    const { content } = this.props;
    return {
      type: 'button',
      props: {
        content
      }
    }
  }
}

由于babel已经成为了开发的标配,所以基本上大家都更习惯于去使用es6 class而不是React.createClass,这种写法当前也不推荐使用了。

在上面的三种Component写法中,最终Component的渲染都会返回一个React Element。但是使用Plain Object去描述React Element会降低开发者开发和阅读的效率,还是借助babel的魔力,我们可以使用JSX来描述我们的React Element。

// function as example
const Button = content => <button>{content}</button>

Component,Element与渲染

让我们回到这个组件树上

const Container = children => <div>{children}</div>
const Header = children => <header>{children}</div>
const Sidebar = children => <nav>{children}</nav>
const Footer = children => <footer>{children}</nav>

 ReactDOM.render(
  (<Container>
    <Header />
    <Sidebar />
    <div>content</div>
    <Footer />
  </Container>), 
  document.querySelector('#container'))

第一层渲染的结果

{
  type: Container,
  props: {
    children: [
      {
        type: Header,
        props: null
      },
      {
        type: Sidebar,
        props: null,
      },
      {
        type: 'div'
        props: {
          children: 'content'
        }
      },
      {
        type: Footer,
        props: null
      }
    ]
  }
}

这时除了div已经是一个Host Element以外,其余的元素都是Component Element,React的渲染机制会让对应的Component会继续递归渲染,直到整个React Element tree最终只剩下Host Element。最终的渲染结果如下。

{
  type: 'div',
  props: {
    children: [
      {
        type: 'header',
        props: null
      },
      {
        type: 'nav',
        props: null,
      },
      {
        type: 'div'
        props: {
          children: 'content'
        }
      },
      {
        type: 'footer',
        props: null
      }
    ]
  }
}

React的渲染机制非常简单但是非常实用,可以大概总结为两点。

  1. 不管是Host Element还是Component Element, React都把他们当成Element用同一种机制去渲染去处理,这也是React的核心思想。
  2. Component会不断进行渲染,直到渲染到children里面没有Component Element为止。

What’s Next?

上面我们提到了React内部其实是递归进行渲染的,你可能会好奇React内部到底是怎么实现这样一套递归渲染机制的。下一篇文章我们就来聊聊这套渲染机制的具体实现–Internal Instances。

https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK