元素渲染

将一个元素渲染为 DOM

假设你的 HTML 文件某处有一个 <div>

  1. <div id="root"></div>

我们将其称为“根” DOM 节点,因为该节点内的所有内容都将由 React DOM 管理。
仅使用 React 构建的应用通常只有单一的根 DOM 节点。如果你在将 React 集成进一个已有应用,那么你可以在应用中包含任意多的独立根 DOM 节点。
想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()

  1. const element = <h1>Hello, world</h1>;
  2. ReactDOM.render(element, document.getElementById('root'));

更新已渲染的元素

考虑一个计时器的例子:

  1. function tick() {
  2. const element = (
  3. <div>
  4. <h1>Hello, world!</h1>
  5. <h2>It is {new Date().toLocaleTimeString()}.</h2>
  6. </div>
  7. );
  8. ReactDOM.render(element, document.getElementById('root'));
  9. }
  10. setInterval(tick, 1000);

这个例子会在 setInterval() 回调函数,每秒都调用 ReactDOM.render()

组件&Props

函数组件与 class 组件

定义组件最简单的方式就是编写 JavaScript 函数:

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }

你同时还可以使用 ES6 的 class 来定义组件:

  1. class Welcome extends React.Component {
  2. render() {
  3. return <h1>Hello, {this.props.name}</h1>;
  4. }
  5. }

上述两个组件在 React 里是等效的。

渲染组件

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }
  4. const element = <Welcome name="Sara" />;
  5. ReactDOM.render(
  6. element,
  7. document.getElementById('root')
  8. );

组合组件

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }
  4. function App() {
  5. return (
  6. <div>
  7. <Welcome name="Sara" />
  8. <Welcome name="Cahal" />
  9. <Welcome name="Edite" />
  10. </div>
  11. );
  12. }
  13. ReactDOM.render(
  14. <App />,
  15. document.getElementById('root')
  16. );

提取组件

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <img className="Avatar"
  6. src={props.author.avatarUrl}
  7. alt={props.author.name}
  8. />
  9. <div className="UserInfo-name">
  10. {props.author.name}
  11. </div>
  12. </div>
  13. <div className="Comment-text">
  14. {props.text}
  15. </div>
  16. <div className="Comment-date">
  17. {formatDate(props.date)}
  18. </div>
  19. </div>
  20. );
  21. }

该组件用于描述一个社交媒体网站上的评论功能,它接收 author(对象),text (字符串)以及 date(日期)作为 props。
该组件由于嵌套的关系,变得难以维护,且很难复用它的各个部分。因此,让我们从中提取一些组件出来。
首先,我们将提取 Avatar 组件:

  1. function Avatar(props) {
  2. return (
  3. <img className="Avatar"
  4. src={props.user.avatarUrl}
  5. alt={props.user.name}
  6. />
  7. );
  8. }

Avatar 不需知道它在 Comment 组件内部是如何渲染的。因此,我们给它的 props 起了一个更通用的名字:user,而不是 author
我们建议从组件自身的角度命名 props,而不是依赖于调用组件的上下文命名。
我们现在针对 Comment 做些微小调整:

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <Avatar user={props.author} />
  6. <div className="UserInfo-name">
  7. {props.author.name}
  8. </div>
  9. </div>
  10. <div className="Comment-text">
  11. {props.text}
  12. </div>
  13. <div className="Comment-date">
  14. {formatDate(props.date)}
  15. </div>
  16. </div>
  17. );
  18. }

接下来,我们将提取 UserInfo 组件,该组件在用户名旁渲染 Avatar 组件:

  1. function UserInfo(props) {
  2. return (
  3. <div className="UserInfo">
  4. <Avatar user={props.user} />
  5. <div className="UserInfo-name">
  6. {props.user.name}
  7. </div>
  8. </div>
  9. );
  10. }

进一步简化 Comment 组件:

  1. function Comment(props) {
  2. return (
  3. <div className="Comment">
  4. <UserInfo user={props.author} />
  5. <div className="Comment-text">
  6. {props.text}
  7. </div>
  8. <div className="Comment-date">
  9. {formatDate(props.date)}
  10. </div>
  11. </div>
  12. );
  13. }

组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。来看下这个 sum 函数:

  1. function sum(a, b) {
  2. return a + b;
  3. }

这样的函数被称为“纯函数”
相反,下面这个函数则不是纯函数,因为它更改了自己的入参:

  1. function withdraw(account, amount) {
  2. account.total -= amount;
  3. }

React 非常灵活,但它也有一个严格的规则:
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。