开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
React 组件,元素和实例 - 技术翻译 - 开源中国社区

React 组件,元素和实例 【已翻译100%】

我是菜鸟我骄傲 推荐于 2年前 (共 10 段, 翻译完成于 01-15) 评论 2
收藏  
85
推荐标签: React 待读
许多React初学者都会混淆组件、实例和元素之间的差异,为什么会有三种不同的术语来指画在屏幕上的东西?

管理实例

如果你是react的初学者,你可能只用过组件类和实例。例如,您可以通过创建一个类来声明一个按钮组件。当app在运行时,你可能会在屏幕上看到这个组件中的几个元素,每个元素都带有各自的属性和当前状态。这是一种传统的面向对象的UI编程。那为什么要创建元素呢?


在传统的UI模型中,需要你来创建和销毁子组件实例。如果一个表单组件想要呈现一个按钮组件,需要创建它的实例并且实时手动更新任何最新消息。


class Form extends TraditionalObjectOrientedView {
  render() {
    // 读取一些数据传递给视图     const { isSubmitted, buttonText } = this.attrs;

    if (!isSubmitted && !this.button) {
      // 表单尚未提交,创建一个按钮
      this.button = new Button({
        children: buttonText,
        color: 'blue'
      });
      this.el.appendChild(this.button.el);
    }

    if (this.button) {
      // The button is visible. Update its text!
      this.button.attrs.children = buttonText;
      this.button.render();
    }

    if (isSubmitted && this.button) {
      // 表单已经提交,销毁按钮
      this.el.removeChild(this.button.el);
      this.button.destroy();
    }

    if (isSubmitted && !this.message) {
      // 表单已经提交,展示成功信息
      this.message = new Message({ text: 'Success!' });
      this.el.appendChild(this.message.el);
    }
  }
}

这是伪代码,但它或多或少是你得到当你写复合UI代码的行为始终以面向对象的方式使用库骨干。

gracesyuan
 翻译得不错哦!

每一个组件都得保留到其DOM节点还有其子组件实例的引用,并且在正确时间对它们进行创建、更新和销毁操作。代码行数量的增长是潜在的组件语句数量的平方,而父组件得直接访问它们的子组件实例,使得将来要对它们进行解耦会很困难。

React 如何不同了呢?

元素来描述树

在React中,由元素来解决问题。元素就是一个单纯的对象,它描述了一个组件实例或者DOM节点机器需要的属性。 它只包含有关于组件类型(例如:一个Button),组件的属性(例如:它的颜色),以及它里面的子元素这些信息。

元素并不是一个实在的实例,而是一种告诉React你想要在屏幕上看到什么东西的方式。你不能在元素上面调用方法。它只是一个不可修改的描述性对象,带有两个属性域:type:string|ReactClass)和props:Object1 。

LeoXu
 翻译得不错哦!

DOM 元素

当一个元素的类型是一个 string,它表示一个含有标签名的 DOM 节点,而 props 对应它的属性。这就是 React 将会渲染的节点,例如:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

这个元素只是通过简单对象的方式表示了下面的 HTML:

<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>

注意元素是怎样嵌套的。按照习惯,当我们想去创建一个元素树时,我们指定一个或多个子元素作为他们含有元素的 children 属性值。

子元素和父元素只是描述而不是实例的目的是什么?当你创建它们的时候,它们不会引用任何在屏幕上显示东西。你可以创建它们,然后不管它们,这都没关系。

React 元素很容易被转换而不需要被解析,当然,它们也是比真实的 DOM 元素轻的多——他们只是对象!

我是菜鸟我骄傲
 翻译得不错哦!

    组件元素

    不过,元素的类型也可以是一个对应到React组件的函数或者类:

{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}

    这是  React  的核心概念。

描述一个组件的元素还是一个元素,就跟一个描述DOM节点的元素没什么两样。它们可以相互嵌套和混合:

 这个特性然你可以用一个带有特殊的 color 属性的Button来定义一个DangerButton,不用担心Button会被渲染成一个DOM(button)、一个<div>还是别的什么东西:

const DangerButton = ({ children }) => ({
  type: Button,
  props: {
    color: 'red',
    children: children
  }
});

你可以在一个单独的元素中混合并匹配DOM和组件元素:

const DeleteAccount = () => ({
  type: 'div',
  props: {
    children: [{
      type: 'p',
      props: {
        children: 'Are you sure?'
      }
    }, {
      type: DangerButton,
      props: {
        children: 'Yep'
      }
    }, {
      type: Button,
      props: {
        color: 'blue',
        children: 'Cancel'
      }
   }]
});

或者如果你选择了JSX的话 JSX:

const DeleteAccount = () => (
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <Button color='blue'>Cancel</Button>
  </div>
);

这样的混合和匹配有助于组件同其它东西的解耦,因为他们可以通过组合同时表达出is-a和has-a的关系:

  • Button 是一个带有特殊属性的DOM <button> 。

  • DangerButton 带有特殊属性的Button。

  • DeleteAccount 在一个<div>中包含一个Button和一个DangerButton.

LeoXu
 翻译得不错哦!

组件封装了元素树

当 React 发现了一个带有函数或者类类型的元素,它就会知道要根据提供的对应 props 请求向那个组件请求其所渲染的元素。

当它发现这个元素时:

{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}



React 会向 Button 请求其所渲染的东西。Button 将会返回这个元素:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}



React 会重复这一过程,知道它已经知悉了页面上每个组件所依赖的 DOM 标记元素。

React 就像是一个小孩,会针对每个“X 是 Y”的命题提出“什么是 Y”这种问题,要你来做出解释,直到他们搞明白了事情的所有细节。

还记得上面的 From 示例吗?它可以用 React 写成下面这个样子1

const Form = ({ isSubmitted, buttonText }) => {
  if (isSubmitted) {
    // Form submitted! Return a message element.
    return {
      type: Message,
      props: {
        text: 'Success!'
      }
    };
  }

  // Form is still visible! Return a button element.
  return {
    type: Button,
    props: {
      children: buttonText,
      color: 'blue'
    }
  };
};



这就是了! 对于一个 React 组件而言,props 是输入,而一个元素树就是输出。

所返回的元素树可以同时包含描述了 DOM 节点的元素,以及描述了其它组件的元素。这样就让你能将 UI 的各个独立部分组装起来,而不用去依赖于它们的内部 DOM 结构。

我们让 React 来创建、更新和销毁实例,用元素来描述它们并将它们从组件那里返回,而 React 会负责管理这些实例。

LeoXu
 翻译得不错哦!

组件可以是类或函数。

在上面的代码中,Form,Message 和 Button 是 React的组件。它们可以像上面提到的那样被写成函数,也可以通过 React.Component 点出类来。这三种声明组件的方式是一样的:

// 1) As a function of props const Button = ({ children, color }) => ({   type: 'button',   props: {     className: 'button button-' + color,     children: {       type: 'b',       props: {         children: children       }     }   } }); // 2) Using the React.createClass() factory const Button = React.createClass({   render() {     const { children, color } = this.props;     return {       type: 'button',       props: {         className: 'button button-' + color,         children: {           type: 'b',           props: {             children: children           }         }       }     };   } }); // 3) As an ES6 class descending from React.Component class Button extends React.Component {   render() {     const { children, color } = this.props;     return {       type: 'button',       props: {         className: 'button button-' + color,         children: {           type: 'b',           props: {             children: children           }         }       }     };   } }

当一个组件被定义成一个类时,它比定义成函数的用处更加广泛些。它可以存储一些本地的状态和当相应的 DOM 节点创建或销毁时执行自定义的逻辑。

一个函数组件不像类组件那样强大,但是它更简单些,行为就像只实现了 render 方法的类组件。除非你需要的功能在类里,否则我们希望你尽量使用函数组件

然而,不管是函数还是类,最重要的是他们都是 React 的组件,它们携带参数作为输入,返回 Element 作为输出。

我是菜鸟我骄傲
 翻译得不错哦!

自上而下的整合

当你像下面这样调用:

ReactDOM.render({
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}, document.getElementById('root'));



React 会根据这些 props 向 From 组件请求其返回的元素树。最终它会以一种相对更简单的原语来“提炼”其对你的组件树的理解:

// React: You told me this...
{
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}

// React: ...And Form told me this...
{
  type: Button,
  props: {
    children: 'OK!',
    color: 'blue'
  }
}

// React: ...and Button told me this! I guess I'm done.
{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}



这就是 React 中所谓整合过程的一部分,这一过程开始于你调用 ReactDOM.render() 或者 setState() 时。在整合的末尾,React 就知道了结果 DOM 树,还有一个像 react-dom 或者 react-native 这样的渲染器,应用了需要更新到 DOM 节点(或者是 React Native 环境下的平台独立的视图)上的必要修改的最小集合。

这种逐步提炼的过程也是 React 容易优化的原因所在。如果你的组件树的某些部分对于 React 的访问效率而言变得过大,你可以告诉他跳过这个“提炼”的过程并且比较这个数的特定部分是不是没有发生改变。如果其是不可修改,那么要 props 有没有发生改变会非常快,因此 React 和不可变的方法在一起能运作的很好,并且最少的工作量就能产生很棒的优化效果。

你可能已经注意到这篇博客的字里行间许多都是关于组建和元素的,而关于其实例并没有聊那么多。事实上,实例比起大多数面向对象 UI 框架中,其在 React 的重要性要小得多。

LeoXu
 翻译得不错哦!

只有当组件以类的形式声明时才有实例,而且你还不能直接创建实例:React 帮你创建实例。当父组件实例关联子组件实例的机制出现时,他们只有在必要时才会使用(如设置域的焦点),一般都会被避免的。

React 小心翼翼的为每个类组件创建实例,因此你可以通过面向对象的方式写一个含有方法和本地状态组件,但是除了这些,实例在 React 的编程模式里不是很重要,而且实例也是被 React 自己管理着。

总结

元素是对你想在屏幕上显示的 DOM 节点或其他组件简单的对象描述。元素可以在他们的参数里包含其他元素。创建一个 React 元素是很容易的。一旦一个元素被创建,将不能在被改变。

元素可以通过多种方式声明。它可以通过类的 reader()方法创建。或者,在简单的情况下,可以通过函数定义。不管怎么样,它使用参数作为输入,返回一个元素树作为输出。

当一个元素接收一些参数作为输入,这是由于一个特殊的父组件返回一个还有 type 和这些参数的元素。这就是为什么人们说在 React 中参数是单向流动的:从父到子。

我是菜鸟我骄傲
 翻译得不错哦!

实例是你在你写的组件类中使用 this 表示的东西,这在存储本地状态和对生命周期内的事件做出反应中是有用的。

函数式组件根本没有实例。类组件有实例,但是你不必自己创建一个组件实例 —— React 自己处理。

最终,使用 React.createElement()JSX, 或者 element factory helper 创建元素。不必通过在代码中写元素的简单类 —— 只要知到它们是隐藏的类就可以了。

深入阅读

我是菜鸟我骄傲
 翻译得不错哦!

1.所有的 React 元素由于安全原因都需要一个额外的 $$typeof: Symbol.for('react.element')字段声明。在上面的例子中遗漏了这段。这个博客的入口为元素使用内联对象,是为了让你知道下面会发生什么,但是代码不能运行,除非对元素你添加了 $$typeof,或者修改代码去使用 React.createElement() 或 JSX。

我是菜鸟我骄傲
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(2)
Ctrl/CMD+Enter

不错,一开始我以为是原创,希望更多人来研究react
棒棒的
顶部