乐趣区

react-官方文档翻译系列

react 官方文档翻译

此文档只是针对自己在学习 react 的过程中进行的翻译。可能不是太专业,但是基于自己的理解,用于往后的查阅来说应该是足够了。

  • JSX 的介绍
  • 渲染元素
  • 组件和 Props
  • State 和生命周期
  • 事件处理

JSX 的介绍

思考以下变量的声明:const element = <h1>Hello, world!<h1>
以上有意思的语法,既不是字符串,也不是 HTML。我们称之为 JSX,它是基于 JavaScript 的扩展语法。我们推荐在 React 中使用 JSX 语法来描述 UI 应该是什么样子的。JSX 也许会让你想到模板
语言,但是它能够很好的与 JavaScript 结合,具有 JavaScript 同样强大的功能。JSX 用于生成 React 的元素(elements),我们将在下一章节探讨如何把它们渲染到 DOM 上。接下来,你将会了解到开始使用 JSX 需要掌握的
一些必要知识。

1. 在 JSX 中嵌入表达式

在 JSX 中可以嵌入任何 JavaScript 表达式,通过使用一对花括号“{}”进行包裹。例如 2 + 2, user.firstName, 和 formatName(user)都是有效的表达式:
      import React from 'react';
      import ReactDom from 'react-dom';
      function formatName(user) {return user.firstName + ' ' + user.lastName;}

      const user = {
          firstName: 'Harper',
          lastName: 'Perez'
      };

      const element = (
          <h1>
              Hello, {formatName(user)}!
          </h1>
      );

      ReactDom.render(
          element,
          document.getElementById('root')
      );
输出结果为:Hello, Harper Perez!
为了方便阅读,我们把 JSX 分割成多行来书写,当然并不是必须的,但是如果要这么做的话,我们还是建议用圆括号括起来,从而避免分号自动
插入的陷阱。

2. JSX 也是一种表达式

编译之后,JSX 表达式就会变成普通的 JavaScript 对象。这意味着你可以在 if 以及 for 语句块里使用 JSX,赋值给一个变量,当做参数传递以及作为函数的返回值:
      function getGreeting(user) {if (user) {return <h1>Hello, {formatName(user)}!</h1>;
        }
        return <h1>Hello, Stranger.</h1>;
      }

3. 使用 JSX 来指定属性值

你可以使用引号来为属性指定字符串常量值:
      const element = <div tabIndex="0"></div>;
你也可以在属性值中使用花括号来嵌套 JavaScript 表达式:
    const element = <img src={user.avatarUrl}></img>;
当使用花括号来嵌套 JavaScript 表达式的时候,不要在花括号外面使用引号来嵌套。否则 JSX 将会把该值当做一个字符串字面量来处理,而不是
表达式。在同一个属性值当中,你可以使用花括号和引号中的任意一个,但是不能两个都同时使用。

4. 使用 JSX 来指定子元素

如果一个标签是空的,你可以使用‘/>’进行标签的闭合。看如下的 XML:
    const element = <img src={user.avatarUrl} />;
JSX 标签可能会包含子元素:
    const element = (
        <div>
          <h1>Hello!</h1>
          <h2>Good to see you here.</h2>
        </div>
      );
  警告:比起 HTML,JSX 更接近于 JavaScript,所以 JSX 采用驼峰式的属性命名约定来代替 HTML 中的属性名。例如在 JSX 中,class 变为 className, tabindx 变为 tabIndex。

5. JSX 阻止了注入攻击

在 JSX 中嵌入用户的输入是安全的:
      const title = response.potentiallyMaliciousInput;
      // This is safe:
      const element = <h1>{title}</h1>;
默认情况下,在渲染之前,React Dom 会把嵌套在 JSX 里的任何值进行转义,也就是意味着,你无法注入任何非明确在应用中指定的东西。在渲染之前,所有的值都会被转换成字符串,这帮助解决了跨站点攻击。

6. JSX 是对象

 Babel 把 JSX 语法编译之后,其实就是调用了 React.createElement().
 以下的两个实例是等效的:
     const element = (
       <h1 className="greeting">
          Hello, world!
       </h1>
     );
     const element = React.createElement(
       'h1',
       {className: 'greeting'},
       'Hello, world!'
     );
    
     React.createElement() 在执行的时候进行了一些校验以帮助我们写出更加健壮的代码,本质上它创建了如下的一个对象:// 注意: 当前这个结构是简化了的
        const element = {
          type: 'h1',
          props: {
            className: 'greeting',
            children: 'Hello, world'
          }
        };
这样的对象我们称之为 React 元素。你可以把它想象为我们在屏幕上所看到元素的一个描述。React 读取这些对象,使用它们来构造 DOM,并且保持实时更新。我们将在下一章节讨论如何把 React 的元素渲染到 DOM 上。提示:我们推荐使用支持 Babel 的编辑器,从而 es6 和 JSX 语法均能够正确的高亮显示。

渲染元素

元素是 React 应用中最小的构建模块。一个元素描述了你想要在屏幕上看到的东西:
      const element = <h1>Hello, world<h1/>;
不像浏览器 DOM 元素那样,React 元素是简单的对象,并且易于创建。(浏览器的元素创建代价比较昂贵)React DOM
兼顾更新 DOM 去匹配 React 元素。注意:另外一个被大家熟知的概念“组件”可能会使元素理解起来比较困惑。我们将在下一章节讲解组件。元素是组件
的组成部分,我们建议在阅读下一章节前仔细阅读该部分的内容。

1. 把元素渲染到 DOM

假设在你的 HTML 文件中有一个 <div> 元素:<div id="root"></div>
我们称之为根元素节点,因为在该元素当中的所有内容将会由 React DOM 所管理。使用 React 构建的应用通常来说只有单一的一个根节点。如果你想在现有的应用中集成 React 的话,那你可以定义多个
孤立的根节点。渲染 React 元素到根 DOM 节点上,把二者都传递给 ReactDom.render():
        const element = <h1>Hello, world</h1>;
        ReactDOM.render(
            element,
            document.getElementById('root')
        );
在页面中将会显示:Hello, world

2. 更新已经渲染的元素

React 元素是不可变的。只要创建一个元素之后,就不能改变它的子元素和属性。一个元素就像是电影中的某一帧:它
代表了在某一个确定的时间点上的 UI 展现。根据我们目前的知识,更新 UI 的唯一途径是创建一个新的元素,传递给 ReactDOM.render()进行渲染。思考以下时钟的示例:
        function tick() {
            var element = (
                <div>
                  <h1>Hello, world!</h1>
                  <h2>It is {new Date().toLocaleTimeString()}.</h2>
                </div>
            );
            ReactDom.render(
                element,
                document.getElementById('root')
            );
        }
        
        setInterval(tick, 1000);
每隔一秒钟 setInteval 的回调就会去调用一次 ReactDom.render()。注意:在实践中,大多数的 React 应用只调用一次 ReactDom.render()。下一个章节中,我们将会学习如何在
有状态的组件(stateful components)中去封装这样的代码。

3. React 只会更新需要更新的元素

React DOM 会比较前一个元素以及它的子元素,并且只会去更新需要更新的 DOM,从而达到预期的效果。你可以运行上一个例子,通过浏览器工具来进行验证。(以下为实例的截图)

尽管我们在每一秒钟的时候创建了一个描述整个 UI 树的元素,但是只有文本节点当中的内容被 React DOM 更新了。

组件和 Props

组件可以让你把 UI 分成独立的,可服用的一些代码块。每一个代码块都可以单独的考虑。在概念上,组件类似于 JavaScript 的函数(function)。他们接收任意的输入(称之为“props”),返回一个 React 元素,描述了屏幕上应该显示的信息。

1. 函数(functional)和类(class)申明的组件

定义组件的最简单的方式是写一个 JavaScript 函数:
      function Welcome(props) {return <h1>Hello, {props.name}</h1>;
      }
这个函数是一个有效的 React 组件,因为它接收一个单一的 props 对象作为参数,并且返回了一个 React 元素。我们称之为
函数化的组件,因为从字面上来看它就是一个 JavaScript 函数。你同样可以使用 es6 的 class 来定义一个组件:
    class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;
          }
      }
 从 React 的角度来看,以上两个组件是等价的。Classes 有一些其他的特性,我们将在下一章节探讨。鉴于函数化的组件简洁,我们将使用它来进行组件的定义。

2. 渲染组件

在上面的示例中,我们仅仅使用到了表示 DOM 标签的 React 元素:
      const element = <div />;
另外,元素也可以是用户自定义的组件:
    const element = <Welcome name="Sara" />;
当 React 遇到用户自定义的组件的时候,它把 JSX 属性当做一个对象传递给组件,我们把改对象称之为“props”。例如,下面的代码将会把“Hello, Sara”渲染到上:
      function Welcome(props) {return <h1>Hello, {props.name}</h1>;
      }
      const element = <Welcome name="Sara" />;
      ReactDOM.render(
        element,
        document.getElementById('root')
      );
让我们回顾下这个示例都发生了什么:1. 我们调用 ReactDom.render()来渲染 <Welcome name="Sara" />
  2. React 调用 Welcome 组件,把 {name: 'Sara'} 作为 props
  3. Welcome 组件返回一个 <h1>Hello, Sara</h1> 元素作为结果。4. React DOM 会及时的更新 DOM 去匹配 <h1>Hello, Sara</h1>
  
警告:组件名称要以大写字母开头。例如:<div /> 表示 DOM 标签,但是 <Welcome /> 代表一个组件。

3. 组件的组合

组件可以被另外一个组件所引用。这使得我们可以在不同层级的详细信息中使用同一个组件。一个 button,form,dialog,screen:在 React 中,这些通常都表现为一个组件。例如,我们可以创建一个多次渲染 Welcome 的组件 App:
      function Welcome(props) {return <h1>Hello, {props.name}</h1>;
      }
      function App() {
          return (
            <div>
              <Welcome name="Sara" />
              <Welcome name="Cahal" />
              <Welcome name="Edite" />
            </div>
          );
      }
      ReactDOM.render(
          <App />,
          document.getElementById('root')
      );
典型的,新的 React 应用会在顶部有一个单一的 App 组件。然而,如果是在已有的项目中集成 React,你可以通过自下而上的方式,从一个小的组件例如 Botton 开始,按照你自己的方式逐渐的由下而上
到达视图的顶部。警告:组件必须返回一个单一根元素的元素。这就是为什么我们添加一个 <div> 来包含所有的 <Welcome />
元素了。

4. 组件的提取

不要担心把组件切分为更小的组件。例如,思考如下的 Comment 组件:
      function Comment(props) {
          return (
            <div className="Comment">
              <div className="UserInfo">
                <img className="Avatar"
                  src={props.author.avatarUrl}
                  alt={props.author.name}
                />
                <div className="UserInfo-name">
                  {props.author.name}
                </div>
              </div>
              <div className="Comment-text">
                {props.text}
              </div>
              <div className="Comment-date">
                {formatDate(props.date)}
              </div>
            </div>
          );
      }
它接收 author(一个对象),text(字符串),以及 date(日期)作为 props,描述了一个社交媒体
网站上的评论。因为嵌套的关系,这个评论组件不好去做改动,也难于重用其中的个人信息部分。让我们从中提取
一些组件。首先,我们将提取 Avatar:
      function Avatar(props) {
          return (
              <img className="Avatar"
              src={props.user.avatarUrl}
              alt={props.user.name}
             />
          );
      }
Avatar 组件并不需要知道在 Comment 组件当中被渲染了。这也是为什么我们把 prop 的 name 定义为
更加通用的 user,而不是 author。我们更加推荐从组件自身的视图角度来命名 props,而不是通过他被使用的场景来定义。现在我们可以稍微的简化下 Comment:
        function Comment(props) {
          return (
            <div className="Comment">
              <div className="UserInfo">
                <Avatar user={props.author} />
                <div className="UserInfo-name">
                  {props.author.name}
                </div>
              </div>
              <div className="Comment-text">
                {props.text}
              </div>
              <div className="Comment-date">
                {formatDate(props.date)}
              </div>
            </div>
          );
        }
下一步我们将要提取 UserInfo 组件,该组件紧挨着用户的姓名,渲染一个 Avatar 组件:
        function UserInfo(props) {
          return (
            <div className="UserInfo">
              <Avatar user={props.user} />
              <div className="UserInfo-name">
                {props.user.name}
              </div>
            </div>
          );
        }
这让我们进一步的简化 Comment 组件:
        function Comment(props) {
          return (
            <div className="Comment">
              <UserInfo user={props.author} />
              <div className="Comment-text">
                {props.text}
              </div>
              <div className="Comment-date">
                {formatDate(props.date)}
              </div>
            </div>
          );
        }
提取组件似乎是一件枯燥乏味的工作,但是在大型的应用中提供了可重用组件的模板。首要的一个原则
就是如果 UI 中的一部分被使用了多次(Button, Panel, Avatar),或者足够的复杂(pp, 
FeedStory,Comment),它将会是可重用组件的最佳候选。

5. Props 是只读的

不管你是通过 function 或者是 class 的方式申明的组件,它从不会修改它自身的 props。思考如下的
Sum 函数:
        function sum(a,b) {renturn a + b;}  
类似这样的函数我们称之为纯粹 (pure) 的函数,因为这样的函数不会试图去改变他们的输入参数,并且相同的输入总是返回相同的结果。相反,如下的函数并不是纯粹的,因为它改变了它的入参。
        function withdraw(account, amount) {account.total -= amount;}
React 非常的灵活,但是它有一条非常严格的规则:所有的 React 组件必须像纯粹的函数一样,不去影响到他们的 props。当然,应用程序的 UI 是随时在变化的。在下一个章节,我们将介绍一个新的概念:state。State 允许
React 组件根据用户的动作、网络的响应以及其他的事件来改变他们的输出,但是并不违背这一原则。待续...
退出移动版